什么是堆栈粉碎(C)?

时间:2016-11-04 06:23:13

标签: c gcc stack-overflow stack-smash

代码:

int str_join(char *a,  const char *b) {
   int sz =0; 
   while(*a++) sz++;  
   char *st = a -1, c;  
   *st = (char) 32;
   while((c = *b++)) *++st = c;  
   *++st = 0;
   return sz;
}

....

char a[] = "StringA"; 
printf("string-1 length = %d, String a = %s\n", str_join(&a[0],"StringB"), a);

输出:

  

string-1 length = 7,char * a = StringA StringB

     

***堆栈粉碎检测****:/ T02终止

     

中止(核心倾销)

我不明白为什么它会显示堆栈粉碎?什么是*堆栈粉碎?或者是我的编译器错误?

2 个答案:

答案 0 :(得分:7)

好吧,堆栈粉碎堆栈缓冲区溢出是一个相当详细的主题,在这里讨论,您可以参考this wiki article了解更多信息。

来到这里显示的代码,问题是,你的数组a不足以保存最终的连接结果。

因此,通过说

 while((c = *b++)) *++st = c;

你实际上是访问了调用undefined behavior的绑定内存。这就是你得到“堆栈粉碎”问题的原因,因为你试图访问不属于你的进程的内存。

要解决此问题,您需要确保数组a包含足够的空间,以便将第一个和第二个字符串连接在一起。简而言之,您必须提供更大的目标数组。

答案 1 :(得分:3)

堆栈粉碎意味着你已经在("粉碎"过去/通过)函数的本地变量存储空间之外写了(这个区域被称为"堆栈&# 34;,在大多数系统和编程语言中)。您可能还会发现这种类型的错误称为"堆栈溢出"和/或"堆栈下溢"。

在您的代码中,C可能会将a指向的字符串放在堆栈中。在您的情况下,导致堆栈粉碎的地方是当您将st增加到原始a指针之外并写入它指向的位置时,您将在C编译器保证的区域外写入保留给分配到a的原始字符串。

每当你在一个已经正确"保留"在C中,那是"未定义的行为" (这只是意味着C语言/标准没有说明会发生什么):通常,你最终会覆盖程序内存中的其他东西(程序通常会将其他信息放在堆栈的变量旁边) ,如返回地址和其他内部细节),或者您的程序尝试在内存外写操作系统已经允许"允许"它可以使用。无论哪种方式,程序通常都会断开,有时会立即显而易见(例如,使用"分段错误"错误),有时以非常隐蔽的方式,直到以后才会变得明显。

在这种情况下,您的编译器正在构建具有特殊保护的程序以检测此问题,因此您的程序将退出并显示错误消息。如果编译器没有这样做,你的程序将尝试继续运行,除非它可能最终做错了和/或崩溃。

解决方案归结为需要明确告诉您的代码有足够的内存用于组合字符串。你可以通过明确指定" a"的长度来做到这一点。数组对于两个字符串都足够长,但这通常只适用于您事先知道需要多少空间的简单用途。对于通用解决方案,您可以使用类似malloc的函数从操作系统获取指向新块内存的指针,该指针具有您需要/想要的大小完整大小(只需记住free来自malloc的指针以及类似的功能(一旦你完成它们)。