c - snprintf c 的奇怪行为

标签 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, &str_r);

execl("./recive.x","./recive.x",&str_w,&str_r,(char *)NULL);

这里重要的是 snprintf:我正在尝试将 vector 中的数字转换为字符串。该数字将小于 10。当我执行此操作时,显示的 printf 的结果是:

5 6  6

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

这到底是什么行为!!

提前谢谢您!

最佳答案

After a few weeks playing with C, I now hate it.

这种 react 并不罕见。我的 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 终止符)。

Which means that there's a number (5) in select[1], there's a number (6) in result[0] and result[0] is converted to string properly but select[1] no.

What the hell is that behaviour!!

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

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

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 字符串。

关于c - snprintf c 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14922004/

相关文章:

c - 如何测量从用户那里获取输入的二进制文件的准确性能(执行时间)

javascript - 表数据上的 G-mail 样式表单提交

c++,尾随逗号如何不是错误,会发生什么?富 x, y,;

generics - [T; 之间有什么区别? N] 和 U 如果 U 总是设置为 [T; N]?

c - fcntl 与 F_SETLK

C - 线程同步

"sizeof(arr[0])"会导致未定义的行为吗?

javascript - "Cannot read property of ' <propName> ' of undefined"即使已定义

c - 如何跳过 C 文件的所有 lint 错误

c++ - 为什么这里需要 std::launder ?