C 无效读/写有时会造成分段错误,有时则不会

C 无效读/写有时会造成分段错误,有时则不会,c,linux,segmentation-fault,C,Linux,Segmentation Fault,示例代码: int main () { char b[] = {"abcd"}; char *c = NULL; printf("\nsize: %d\n",sizeof(b)); c = (char *)malloc(sizeof(char) * 3); memcpy(c,b,10); // here invalid read and invalid write printf("\nb: %s\n",b); printf("\nc: %s\n",c); r

示例代码:

int main ()
{
  char b[] = {"abcd"};
  char *c = NULL;
  printf("\nsize: %d\n",sizeof(b));
  c = (char *)malloc(sizeof(char) * 3);
  memcpy(c,b,10);   // here invalid read and invalid write
  printf("\nb: %s\n",b);
  printf("\nc: %s\n",c);

  return 0;
}
在代码中,我做了一些无效读取和无效写入,但这个小程序运行良好,不会创建
核心转储

但在我的大库中,每当我进行1字节无效读或无效写时,它总是创建核心转储

问题:

int main ()
{
  char b[] = {"abcd"};
  char *c = NULL;
  printf("\nsize: %d\n",sizeof(b));
  c = (char *)malloc(sizeof(char) * 3);
  memcpy(c,b,10);   // here invalid read and invalid write
  printf("\nb: %s\n",b);
  printf("\nc: %s\n",c);

  return 0;
}

为什么有时我会从无效读/写中获取核心转储,有时我不会获取核心转储?

这完全取决于执行无效读/写时覆盖或取消引用的内容。具体地说,如果您覆盖某个被取消引用的指针,例如,一个指针的最高有效字节,那么最终可能会导致某个指针被取消引用到完全不同(并且完全无效)的内存区域


因此,例如,如果堆栈的排列方式使memcpy超过
c
的结尾将覆盖
b
的一部分,那么当您尝试使用
b
作为参数调用
printf()
时,它将尝试使用该指针并取消引用以打印字符串。因为它不再是有效的指针,这将导致segfault。但是,由于堆栈排列之类的事情依赖于平台(可能还有编译器),因此在不同的程序中,类似的示例可能看不到相同的行为。

您创建了一个3个字符的字符串
c
,但您在其上复制了10个字符。这是一个错误

这称为缓冲区溢出:您在不属于您的内存中写入。因此,行为是未定义的。它可能是一个崩溃,它可以正常工作,或者它可以修改您创建的另一个变量

所以最好的办法是为c分配足够的内存来包含b的内容:

c = (char *)malloc(sizeof(char) * (sizeof(b)+1)); // +1 is for the '\0' char that ends every string in c.
2-当您在
c
中复制
b
时,不要忘了在字符串的末尾加上字符:
'\0'
。这在c标准中是强制性的。 所以
printf(“%s”,c)知道字符串在哪里结束

3-您将10个字符从
b
复制到
c
,但
b
仅包含5个字符(a、b、c、d和'\0'),因此
memcpy
的行为未定义(例如:memcpy可以尝试读取无法读取的内存,…)

您只能复制自己拥有的内存:
b
的5个字符


4-我认为定义
b
的好方法是:
char b=“abcd”
字符b={'a','b','c','d',0}

您试图做的基本上是缓冲区溢出&在您的代码示例中更具体地说。只有在某些时候才会看到崩溃的原因取决于您正在访问的内存区域&您是否有访问/写入它的权限(Dan Fego对此做了很好的解释)。我认为Dan Fego提供的示例更多地是关于堆栈溢出的(欢迎更正!)。gcc具有与堆栈上的缓冲区溢出(堆栈崩溃)相关的保护。您可以在以下示例中看到这一点(基于堆栈的溢出):

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

int main (void)
{
    char b[] = { "abcdefghijk"};
    char c [8];
    memcpy (c, b, sizeof c + 1);      // here invalid read and invalid write
    printf ("\nsize: %d\n", sizeof b); 
    printf ("\nc: %s\n", c); 
    return 0;
}
可以使用gcc中的
-fno stack protector
选项禁用此保护。
缓冲区溢出是造成安全漏洞的主要原因之一。不幸的是,像
memcpy
这样的函数不会检查这些类型的问题,但是有很多方法可以防止这些类型的问题。

希望这有帮助

+1,你说得对;位于
c
的内存位于堆上,而
b
位于堆栈上。因此,在这种情况下,你是溢出堆。接得好