为什么我的程序中会出现这个堆栈粉碎错误?

问题描述

我正在练习在 C 中使用 strcpy() 并编写了这个程序。

#include <stdio.h>
#include <string.h>

int main(){


   int array[4] = {3,7,1,5};
   char str[4],temp[5];


   for (int i = 0; i < 4; i++){
       sprintf(&str[i],"%d",array[i]);
   }

   for (int i = 0; i < 4; i++){

       strcpy(&temp[i],&str[i]);
       printf("%c",temp[i]);
   }



     return 0;

}
~                

不幸的是,我收到了“stack smashing”错误,这是为什么?

解决方法

Strings 在 C 中必须以 NUL 字节 ('\0') 结尾,并且在调整缓冲区大小时必须考虑到这一点。

给定array的内容,如下代码

for (int i = 0; i < 4; i++) {
   sprintf(&str[i],"%d",array[i]);
}

将按如下方式写入字节,记住数组是从 0 索引的:

  • '3' 到第 0 个索引,'\0' 到第 1 个索引
  • '7' 到第 1 个索引,'\0' 到第 2 个索引
  • '1' 到第二个索引,'\0' 到第三个索引
  • '5' 到第 3 个索引,'\0' 到第 4 个索引

您用当前整数覆盖先前放置的 NUL。不过,你看到问题了吗? str[4] 中没有第四个索引。您将向内存写入一个不应访问的字节。


即使使用 str[5],为最终的 NUL 留出空间,您的代码的第二部分也会以非常适得其反的方式工作。

第一个循环的结果将使 str 的内容等于 "3714",这样

strcpy(&temp[i],&str[i]);

然后将这样复制字符串:

  • "3714" 到温度 + 0
  • "714" 到温度 + 1
  • "14" 到温度 + 2
  • "4" 到温度 + 3

看看你是如何沿着相同的字符串行走,将相同的尾巴重新复制到相同的位置? printf("%c",temp[i]); 只是打印您所在尾部的第一个字符。

str 中有足够的空间,这个程序本质上是一种非常圆润的书写方式:

int array[] = { 3,7,1,5 };

for (int i = 0; i < 4; i++)
    printf("%d",array[i]);

此外,如果 array 包含任何整数,其表示为字符串需要多个字节(例如,-532),则此代码根本无法缩放.


您需要将 str 更改为二维数组,其中包含您将从 array 中的整数创建的所有字符串的空间

#include <stdio.h>
#include <string.h>

int main(void) {
   int array[4] = {3,5};
   char str[4][12],temp[12];

   for (int i = 0; i < 4; i++)
       sprintf(str[i],array[i]);

   for (int i = 0; i < 4; i++){
       strcpy(temp,str[i]);
       printf("[%d] %s\n",i,temp);
   }
}

虽然这对于 strcpy 的使用来说仍然不是一个很有趣的练习,因为我们可以直接打印 str[i]

这是一个在字符串表示形式超过两个字符的数组中搜索最后一个整数时使用 strcpy 的示例。

#include <stdio.h>
#include <string.h>

int main(void) {
    int array[] = { 3,123,4,13,-55,29,55 };

    char string[12] = "0",temp[12];

    for (int i = 0; i < (sizeof array / sizeof *array); i++)
        if (sprintf(temp,array[i]) > 2)
            strcpy(string,temp);

    printf("Final value: %s\n",string);
}
,

每个str[i]只能容纳一个字符,不能容纳一个字符串——当你写

sprintf( &str[i],array[i] );

您正在尝试将 2 个字符的字符串1 写入 1 个字符的缓冲区;字符串终止符“溢出”到数组元素 str[i+1] 中,直到到达 str[3],在这种情况下,终止符溢出数组的末尾。

要存储一个字符串数组,需要留出一个char的二维数组:

char str[4][N+1]; // can hold 4 strings of length N,+1 for the string terminator

然后在你的 sprintf 语句中,你会写

sprintf( str[i],array[i] ); // no & operator on str[i]

  1. array 中的所有值都只有一位 - 对于更大的值,您的数组中需要更多的字符。一个 32 位 int 的字符串表示最多可以有 10 个数字字符,加上一个符号字符,加上字符串终止符,所以你需要留出一个可以容纳 12 个元素的数组,所以理想情况下 {{1 }} 应该声明为
    str