为什么strcpy触发带有全局变量的分段错误?

时间:2020-03-06 14:37:05  来源:igfitidea点击:

所以我有一些C代码:

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

/* putting one of the "char*"s here causes a segfault */
void main() {
  char* path = "/temp";
  char* temp;
  strcpy(temp, path);
}

这将按照其外观进行编译,运行和运行。但是,如果将一个或者两个字符指针声明为全局变量,则strcpy会导致分段错误。为什么会这样?显然,我对范围的理解有误。

解决方案

我们忘记分配和初始化temp:

temp = (char *)malloc(TEMP_SIZE);

只要确保TEMP_SIZE足够大即可。我们还可以在运行时计算此值,然后确保大小足够(应至少为strlen(path))

要注意的重要部分:
目标字符串dest必须足够大才能接收副本。
在情况下,temp没有分配要复制到的内存。

从strcpy的手册页中复制:

DESCRIPTION
   The  strcpy()  function  copies the string pointed to by src (including
   the terminating '
// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, 256, path);

// Or, use dynamic memory
char *temp = (char *)malloc(256);
strncpy(temp, 256, path);
' character) to the array pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy.

temp变量不指向任何存储(内存),并且未初始化。

如果temp声明为char temp [32];,那么无论声明在哪里,代码都将起作用。但是,用这样的固定大小声明温度还存在其他问题,但这又是一个问题。

现在,为什么在全局而不是局部声明时会崩溃。运气...

当在本地声明时,temp的值来自那时堆栈上可能有的值。幸运的是,它指向的地址不会导致崩溃。但是,它浪费了其他人使用的内存。

在全局声明时,在大多数处理器上,这些变量将存储在使用零需求页面的数据段中。因此,char * temp看起来好像已被声明为" char * temp = 0"。

我们正在调用未定义的行为,因为我们没有初始化temp变量。它指向内存中的随机位置,因此程序可能会运行,但很可能会出现段错误。我们需要将目标字符串设为数组,或者使其指向动态内存:

// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, sizeof(temp), path);
temp[sizeof(temp)-1] = '
1. don't have magic numbers laced through the code, and
2. you guarantee that your string is null terminated.
';

同样,使用strncpy()代替strcpy()以避免缓冲区溢出。

如上所述,我们忘记为temp分配空间。
我更喜欢strdup而不是malloc + strcpy。它可以完成我们想做的事情。

不,无论变量如何,它都不会起作用,因为我们很幸运。我们需要分配空间来存储字符串的内容,而不是保留未初始化的变量。

堆栈上未初始化的变量将指向几乎随机的内存位置。如果这些地址碰巧是有效的,那么代码将遍历所有内容,但不会出现错误(但可能会在代码中的其他位置出现与内存破坏相关的令人讨厌的错误)。

全局变量始终失败,因为它们通常设置为指向未映射内存的特定模式。尝试取消引用这些引用会立即给我们带来一个段错误(最好将其留给以后使该错误很难被跟踪)。

正如其他张贴者所提到的那样,问题的根源在于温度尚未初始化。当在堆栈上声明为自动变量时,它将包含该内存位置中发生的任何垃圾。显然,对于我们正在运行的编译器+ CPU + OS,该位置的垃圾是有效指针。 strcpy"成功"之处在于它没有段错误,但实际上它已将字符串复制到内存中其他位置的任意位置。这种内存破坏问题使调试变得异常困难,这在世界各地的C程序员中都引起了恐惧。

将临时变量声明移到全局范围时,它将放置在BSS部分中并自动清零。尝试取消引用* temp会导致段错误。

当我们将* path移至全局作用域时,* temp会在堆栈中向上移动一个位置。该位置上的垃圾显然不是有效的指针,因此取消引用* temp会导致段错误。

我想先将亚当的片段改写为

##代码##

这样,我们:

##代码##

第二点是如果源字符串的长度大于等于256个字符,则会丢失最后一个字符。