我正在学习静态变量,并且想了解为什么 我犯的错误导致了它的行为。
我有一个返回字符串的函数:
char *hhmmss(int sec) {
static char s[9];
// hr, min, sec calcs
sprintf(s, "%02d:%02d:%02d%c", hr, min, sec, '\0'); // intentional mistake
return s;
}
如您所见,我最初以为我必须在 使用sprintf。
程序还具有其他功能,其中两个也使用静态变量。
现在这里很奇怪-我的程序按预期工作,直到我 只需将函数从程序末尾移到函数前 叫它。我没有做其他更改。这两个函数使用 现在,静态变量会在每次调用静态变量时重置它们, 而不是在程序运行时保留它们的值。
我花了很长时间才弄清楚这是偶然添加的 导致此问题的空字符。有人可以解释一下实际上是什么 要在这里吗?
答案 0 :(得分:3)
您正在编写超出数组结尾的位置。
s
包含9个字符。您的格式字符串将总共写入10个字节:2位数字,:
,2位数字,:
,2位数字,一个显式0字节和一个隐式0字节。这样就注销了数组的末尾。这样做会调用undefined behavior。这体现在您的程序中,它似乎在移动变量之前就起作用了,而一个不相关的变量在您移动它之后就被更改了。
可以通过使s
足够大以容纳多余的字符,或者通过从格式字符串中删除不需要的多余字符来解决此问题。
答案 1 :(得分:2)
sprintf
调用从&s[0]
开始总共向内存写入10个字符,该内存仅可容纳9个空间。这是“未定义的行为”,意味着可能发生任何事情。
在实践中,其他一些具有静态存储持续时间的变量可能会在s
存储之后立即出现,并且该值将被覆盖。或者,可能是程序内部使用的一些数据,用于跟踪功能局部static
变量是否已被初始化或需要对其进行首次初始化。 C代码的顺序可以更改编译器决定如何在程序中放置此数据的方式,这可能就是为什么更改该顺序会给您带来不同的结果的原因。
答案 2 :(得分:2)
溢出一个错误:“%02d:%02d:%02d%c”确实格式化9个字符,条件是每个%d值都不会溢出。问题在sprintf()添加自己的\ 0时出现总共10个字符。
这个额外的\ 0溢出s [9]并唤醒其后的所有内容。这种“轻微的溢出”会导致未定义的行为……这取决于编译器放置在数组之后的内容,这取决于变量在内存中的位置。从未被发现到段故障的所有事情都可能发生。
寿命最长的静态内存溢出更容易被发现。这取决于引用已变为零的字节的损坏内存的内容。如果这是另一个字符串的第一个字符,则覆盖的字符串将变为零长度。