为什么我在进行memcpy时会检测到堆栈粉碎?

时间:2012-09-26 06:42:08

标签: c memory-management

    char test[]={"abcde"};
    char* test1={"xyz"};
    memcpy(test+5,test1,3);
    printf("%s",test);

我正在尝试掌握memcpy的确切工作方式,这是我到目前为止所写的例子。 这会将输出设为abcdexyz&vjunkcharacters

以及以下消息。

*** stack smashing detected ***: ./testcode terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xb7656dd5]
/lib/i386-linux-gnu/libc.so.6(+0xffd8a)[0xb7656d8a]
./testcode[0x8048797]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb75704d3]
./testcode[0x80483a1]

这种情况背后的原因是什么?

5 个答案:

答案 0 :(得分:4)

根本原因:

char test[]={"abcde"};

分配足够的内存空间以仅存储5个字符。

memcpy(test+5,test1,3);  

test1指向的数据复制到分配的内存空间之外 从技术上讲,以这种方式超出已分配内存的范围是未定义行为,这意味着任何事情都可能发生。

实际发生了什么?

这里实际发生的是memcpy复制超出分配内存的字符,从而覆盖标记字符数组NULL末尾的test终结符。
此外,printftest的起始地址读取内容,直到遇到随机NULL,从而打印出垃圾字符。

<强>解决方案:
在执行memcpy之前,应确保目标缓冲区已分配足够的内存。由于您打算复制3个字符,因此您的目标缓冲区test应至少为:

5 + 3 + 1 byte for NULL terminator = 9 bytes

您可以简单地使用:

char test[9]="abcde";

答案 1 :(得分:2)

您的memcpy电话确实粉碎了堆叠,这就是您看到该消息的原因。您正在复制数据超过test数组的末尾,这是不允许的。

答案 2 :(得分:2)

在没有额外缓冲区的情况下进行

事实上,最直接的方法是避免副本:

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

int main() {
  char a[] = "abcde";
  char b[] = "xyz";
  printf("%s%s\n", a, b);
  return 0;
}

使用memcpy执行

memcpy将n字节从src复制到dest。您需要自己跟踪正确复制字符串的空终止字节。

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

int main() {
  char a[] = "abcde";
  char b[] = "xyz";

  /* note that both strings add a '\0' termination */
  char c[sizeof(a) + sizeof(b) - 1];

  /* copy the content of a to c */
  memcpy(c, a, sizeof(a));

  /* copy the content of b to where a ends (concatenate the strings) */
  memcpy(c + sizeof(a) - 1, b, sizeof(b));

  /* note that the '\0' termination of the string is necessary to let
   * functions like printf know where the string is over 
   */
  printf(c);

  return 0;
}

使用strcpy和strcat执行

请注意,使用memcpy时,有很多陷阱正确处理字符串的空终止。要简化字符串的此过程,您应该执行以下操作。

如果这些确实是字符串而不是随机字节,那么您应该坚持使用标准库的字符串函数。这就是它的完成方式。

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

int main() {
  char a[] = "abcde";
  char b[] = "xyz";

  /* note that both strings add a '\0' termination */
  char c[sizeof(a) + sizeof(b) - 1];

  /* copy the content of a to c */
  strcpy(c, a);

  /* copy the content of b to where a ends (concatenate the strings) */
  strcat(c, b);

  /* note that the '\0' termination of the string is necessary to let
   * functions like printf know where the string is over 
   */
  printf(c);

  return 0;
}

了解字符串的大小

关于了解缓冲区的大小,请注意您通常不能简单地执行sizeof(a_string)。如果将字符数组传递给函数,它会衰减到指针,并且此操作不再返回数组的预期大小,而是返回指针的大小。

对于字符串,您需要发出strlen(a_string)来扫描空终止的发生并返回字符串的长度(不包括终止)。

对于包含随机数据(或需要写入的空缓冲区)的字符缓冲区,这种方法也不起作用。您总是需要将缓冲区的大小作为附加参数传递。

答案 3 :(得分:1)

memcpy(test+5,test1,3);行用简单的词语执行以下操作:

“从数组”test“的最后一个元素开始,将数组”test1“中的3个字符复制到”,“它基本上写出超出数组'test'长度的2个字符。

所以如果你只是想玩'memcpy'来定义第三个数组:

char test[]="abcde";
char test1[]="xyz";
char output[sizeof(test) + sizeof(test1)];
memset(output, 0, sizeof(output));
memcpy(&output[0],test,5);
memcpy(&output[5],test1,3);
printf("%s",output);

答案 4 :(得分:1)

变量test1在内存中4个字符,3个加上结束字符串终止符。试试这个:

char test[9]={"abcde"};
char* test1={"xyz"};
memcpy(test+5,test1,4);