奇怪的行为snprintf c

时间:2013-02-17 14:17:55

标签: c undefined-behavior

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

我现在正试图使用​​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

这意味着select [1]中有一个数字(5),result [0]中有一个数字(6),result [0]正确转换为字符串,但选择[1] no。

这种行为到底是什么!!

提前谢谢!

3 个答案:

答案 0 :(得分:2)

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

char str_w[2];
snprintf(str_w, 2, "%d", select[0]);

将一位数字转换为字符串的更好方法如下:

char res[2];
res[0]=num+'0';
res[1]=0;

请注意第一个零点周围的单引号:想法是将零字符的代码添加到一位数字。

答案 1 :(得分:2)

  

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

这不是一种罕见的反应。我的介绍CS课程的三分之一改变了专业,理由是C的难度(这是一种可怕的教学语言)。我用了好几年的时间来充分包裹它,但是一旦我这样做,我开始欣赏它。

char str_w,str_r;

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

这是你的主要问题; str_wstr_r只能保留一个char值,而不是字符串(这需要至少 2个字符)。相反,您需要将str_wstr_r声明为char数组,大到足以保存最大int你的字符串表示形式我期待,加上符号的空间(如果值为负),再加上0终结符。例如,如果您没有限制selectresult上的值:

#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]);

通过使目标数组足够大以进行任何可能的输入,您不必担心溢出。是的,您会受到一些内部碎片的影响,并且取决于您的应用程序,这可能是也可能不是问题。但我只想保持简单。

如果您知道某个事实您的selectresult数组永远不会保存0..10范围之外的值,那么您可以将SIZE设置为3(最多2位数加0终结符)。

  

这意味着select [1]中有一个数字(5),result [0]中有一个数字(6),result [0]正确转换为字符串,但选择[1] no。

     

这种行为到底是什么!!

由于您将地址传递给不足以保存结果的缓冲区,因此行为是 undefined ,这意味着编译器没有义务警告您正在执行某些操作危险的。

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

Item        Address        Value
----        -------        -----
str_r       0xffec1230     ??
str_w       0xffec1231     ??
            0xffec1232     ??

str_wstr_r被分配给连续的字节,它们的初始值是不确定的。在第一个snprintfstr_w之后,您的记忆现在看起来像这样:

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

snprintf会将一个尾随的0终结符写入缓冲区;在这种情况下,它将终结符写入str_w之后的字节。在第二次sprintf调用之后,内存现在看起来像这样:

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

第二次snprintf调用将{0}终止符写入str_r之后的字节,恰好是str_w;你结束了之前写给它的价值。这就是为什么你看到str_r字符串而不是str_w字符串的原因。

答案 2 :(得分:0)

使用char定义唯一的一个字节变量。它必须是char s。

的数组