C 奇怪的行为

C 奇怪的行为,c,undefined-behavior,C,Undefined Behavior,玩了几个星期C之后,我现在讨厌它了 我现在正试图使用execl将参数传递给另一个程序,参数的格式会产生奇怪的事情: int select[2], result[2]; char str_w,str_r; snprintf(&str_w, 2, "%d", select[1]); snprintf(&str_r, 2, "%d", result[0]); printf("%d %d %s %s\n", select[1], result[0], &str_w, &

玩了几个星期C之后,我现在讨厌它了

我现在正试图使用execl将参数传递给另一个程序,参数的格式会产生奇怪的事情:

int select[2], result[2];
char str_w,str_r;

snprintf(&str_w, 2, "%d", select[1]);
snprintf(&str_r, 2, "%d", result[0]);
printf("%d %d %s %s\n", select[1], result[0], &str_w, &str_r);

execl("./recive.x","./recive.x",&str_w,&str_r,(char *)NULL);
这里重要的是snprintf:我试图将向量中的数字转换为字符串。这个数字将小于10。执行此操作时,printf的结果如下所示:

5 6  6
这意味着选择[1]中有一个数字(5),结果[0]中有一个数字(6),结果[0]正确地转换为字符串,但选择[1]否

那他妈的是什么行为


提前谢谢你

使用
char
只定义一个字节的变量。它必须为我提供一个字符数组。

对sprintf的调用必须为打印数据提供一个缓冲区,足以满足整个输出。您正在传递一个指向单个字符的指针,因此输出显然不适合

char str_w[2];
snprintf(str_w, 2, "%d", select[0]);
将一位数转换为字符串的更好方法如下:

char res[2];
res[0]=num+'0';
res[1]=0;
Item        Address        Value
----        -------        -----
str_r       0xffec1230     ??
str_w       0xffec1231     ??
            0xffec1232     ??
注意第一个零的单引号:想法是将零字符的代码添加到一位数

玩了几个星期C之后,我现在讨厌它了

这并非罕见的反应。我的intro-CS课程有三分之一的学生换了专业,理由是C语言很难(这是一种可怕的教学语言)。我花了好几年的时间才完全明白这一点,但一旦我明白了,我就开始欣赏它了

char str_w,str_r;

snprintf(&str_w, 2, "%d", select[1]);
snprintf(&str_r, 2, "%d", result[0]);
这是你的主要问题
str_w
str_r
的大小仅足以容纳单个
char
值,而不是字符串(至少需要2个字符)。相反,您需要将
str_w
str_r
声明为
char
数组,其大小足以容纳所需的最大
int
的字符串表示形式,加上符号空间(如果值为负值)和0终止符。例如,如果未限制
上的值,请选择
结果

#define MAX_DIGITS 20 // max decimal digits for 64-bit integer
#define SIZE MAX_DIGITS+2 // 2 extra for sign and 0 terminator

char str_w[SIZE], str_r[SIZE]; 

sprintf(str_w, "%d", select[1]);
sprintf(str_r, "%d", result[0]);
通过使目标数组足够大以容纳任何可能的输入,您不必担心溢出。是的,您会遇到一些内部碎片问题,这取决于您的应用程序是否存在问题。但我只是喜欢把事情简单化

如果您知道您的
选择
结果
数组永远不会保存范围0..10以外的值,那么您可以将大小设置为3(最多2位数字加0终止符)

这意味着选择[1]中有一个数字(5),结果[0]中有一个数字(6),结果[0]正确地转换为字符串,但选择[1]否

那他妈的是什么行为

由于您向缓冲区传递的地址不够大,无法保存结果,因此行为未定义,这意味着编译器没有义务警告您正在执行危险的操作

以下是最有可能发生的情况(由于行为未定义,任何事件序列都是可能的,但我认为这是对结果的合理解释)。首先,假设变量在内存中的布局如下:

char res[2];
res[0]=num+'0';
res[1]=0;
Item        Address        Value
----        -------        -----
str_r       0xffec1230     ??
str_w       0xffec1231     ??
            0xffec1232     ??
str_w
str_r
分配给连续字节,其初始值不确定。在第一次
snprintf
stru w
之后,您的内存现在如下所示:

Item        Address        Value
----        -------        -----
str_r       0xffec1230     ??
str_w       0xffec1231     '5'
            0xffec1232     0
Item        Address        Value
----        -------        -----
str_r       0xffec1230     '6'
str_w       0xffec1231     0
            0xffec1232     0
snprintf
将向缓冲区写入尾随的0终止符;在本例中,它将终止符写入
str\u w
后面的字节。在第二次
sprintf
调用之后,内存现在如下所示:

Item        Address        Value
----        -------        -----
str_r       0xffec1230     ??
str_w       0xffec1231     '5'
            0xffec1232     0
Item        Address        Value
----        -------        -----
str_r       0xffec1230     '6'
str_w       0xffec1231     0
            0xffec1232     0

第二个
snprintf
调用将0终止符写入
stru r
后面的字节,该字节恰好是
stru w
;您最终会破坏之前写入的值。这就是为什么您看到的是
str\u r
字符串,而不是
str\u w
字符串

在玩了几周C之后,我现在讨厌它也许你应该停止玩,先学习一下它?当我玩C的时候,我发现一些困难点让我停下来,思考和学习,所以当我玩的时候,我明白了。如果你真的花了几个星期的时间犯了这些错误,那么你就做错了。但究竟为什么一个有效而另一个无效!!!如果它是错误的,它不应该在任何情况下工作。这正是让我困惑和愤怒的地方。@markmb第一个有效的原因是stru_w后面碰巧有一个额外的字符空间,即分配给stru_r的字符空间。尽管如此,它仍然是未定义的行为,但从本质上讲,它不会因错误而崩溃。