这是我的gcc版本:
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
我有一个简单的C程序,它将源字符串连接到目标。
#include<stdio.h>
void Strcat(char *t, char *s){
while(*t++);
t--;
while(*t++=*s++);
}
void main(void){
char target[8] = "stack";
char *source = "overflow";
printf("%s\n", target);
Strcat(target,source);
printf("%s\n", target);
}
上面的程序给出了这个输出错误:
$ a.out
stack
stackoverflow
*** stack smashing detected ***: a.out terminated
Aborted (core dumped)
但是当我初始化大小为9而不是8的目标数组时,如下所示,该程序产生正确的输出。
#include<stdio.h>
void Strcat(char *t, char *s){
while(*t++);
t--;
while(*t++=*s++);
}
void main(void){
char target[9] = "stack";
char *source = "overflow";
printf("%s\n", target);
Strcat(target,source);
printf("%s\n", target);
}
有人可以解释为什么第一个变体会产生堆栈粉碎错误吗?
答案 0 :(得分:4)
在两者的情况下,您的目标太小而无法保存连锁结果。因此,在Strcat()
函数内部,您将访问超出范围的内存,从而导致undefined behavior。
一旦你点击UB,就没有解释原因,因为,没有明确的方法来解释结果。
因此,在传递目标以存储连接结果之前,你需要确保目标有足够的内存来存储连接结果,以及空终止符。
答案 1 :(得分:2)
这两个程序都有未定义的行为,因为数组target
之外的内存被覆盖,错误消息说明了这一点。
第二个程序给出预期结果的原因可以通过以下方式解释。
将类型为source
的变量char *
放置在内存中,以便在变量target
之后正确对齐指针。因此,变量taget
被声明为
char target[9] = "stack";
^^^
编译器可以在内存中附加额外的字节,以便为以下变量提供所需的对齐
char *source = "overflow";
这些额外的字节可以由函数Strcat
使用。
答案 2 :(得分:0)
两个程序都是UB,但在第二种情况下,堆栈防护在下一个堆栈对齐后放置。通常是8个字节。所以你没有覆盖警卫而你没有得到错误。