在玩了几个星期 C 之后,我现在讨厌它。
这种反应并不少见。我的 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
and声明str_r
为 的数组,char
足够大以容纳int
您所期望的最大字符串表示形式,加上符号空间(如果值为负数),再加上 0 终止符。例如,如果您没有限制select
or的值result
:
#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]);
通过使您的目标数组足够大以容纳任何可能的输入,您不必担心溢出。是的,您会遭受一些内部碎片,并且取决于您的应用程序,这可能是也可能不是问题。但我只是喜欢保持简单。
如果您知道您的select
和result
数组永远不会保存 0..10 范围之外的值,那么您可以将 SIZE 设置为 3(最多 2 位数字加上 0 终止符)。
这意味着 select[1] 中有一个数字 (5),result[0] 中有一个数字 (6),并且 result[0] 被正确转换为字符串,但 select[1] 没有。
这到底是什么行为!!
由于您将地址传递给不足以容纳结果的缓冲区,因此行为是undefined,这意味着编译器没有义务警告您您正在做一些危险的事情。
这是最有可能发生的事情(由于行为未定义,任何事件序列都是可能的,但我认为这是对结果的合理解释)。首先,假设您的变量在内存中的布局如下:
Item Address Value
---- ------- -----
str_r 0xffec1230 ??
str_w 0xffec1231 ??
0xffec1232 ??
str_w
并str_r
分配给连续的字节,它们的初始值是不确定的。在第一个snprintf
to之后str_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
。