偶然的空字符导致静态变量在C中重置

时间:2018-10-08 13:31:11

标签: c

我正在学习静态变量,并且想了解为什么 我犯的错误导致了它的行为。

我有一个返回字符串的函数:

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。

程序还具有其他功能,其中两个也使用静态变量。

现在这里很奇怪-我的程序按预期工作,直到我 只需将函数从程序末尾移到函数前 叫它。我没有做其他更改。这两个函数使用 现在,静态变量会在每次调用静态变量时重置它们, 而不是在程序运行时保留它们的值。

我花了很长时间才弄清楚这是偶然添加的 导致此问题的空字符。有人可以解释一下实际上是什么 要在这里吗?

3 个答案:

答案 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]并唤醒其后的所有内容。这种“轻微的溢出”会导致未定义的行为……这取决于编译器放置在数组之后的内容,这取决于变量在内存中的位置。从未被发现到段故障的所有事情都可能发生。

寿命最长的静态内存溢出更容易被发现。这取决于引用已变为零的字节的损坏内存的内容。如果这是另一个字符串的第一个字符,则覆盖的字符串将变为零长度。