不久之前,我在编写一个大数据库的时候正在寻找一个bug,它耗费了我很长时间。问题在于我违反了某些结构成员的内存边界,而不是segmentation fault
或者只是一个普通的崩溃,它做了一些意想不到的事情(至少我没有想到它)。让我介绍一个例子:内存分配违规后free()的奇怪行为
segmentation_fault.c
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#define N 100 /* arbitrary large number */
typedef unsigned char byte;
void exitError(char *);
void segmentationFaultSignalHandler(int);
sig_atomic_t segmentationFaultFlag = 0;
int main(void)
{
int i, memorySize = 0;
byte *memory;
if (setvbuf(stdout, NULL, _IONBF, 0))
exitError("setvbuf() failed");
if (signal(SIGSEGV, segmentationFaultSignalHandler) == SIG_ERR)
exitError("signal() failed");
for (i = 0; i < N; ++i)
{
printf("Before malloc()\n");
if ((memory = malloc(++memorySize * sizeof(byte))) == NULL)
exitError("allocation failed");
printf("After malloc()\n");
printf("Before segmentation fault\n");
memory[memorySize] = 0x0D; /* segmentation fault */
if (segmentationFaultFlag)
exitError("detected segmentation fault");
printf("After segmentation fault\n");
printf("Before free()\n");
free(memory);
printf("After free()\n");
}
return 0;
}
void segmentationFaultSignalHandler(int signal)
{
segmentationFaultFlag = 1;
}
void exitError(char *errorMessage)
{
printf("ERROR: %s, errno=%d.\n", errorMessage, errno);
exit(1);
}
正如我们所看到的线memory[memorySize] = 0x0D;
显然是违反了由malloc()
给出的内存边界,但它不会崩溃或提高的信号(我根据ISO C99/ISO C11了解,信号处理是实现定义的,并且在违反内存边界时根本不需要加任何处理)。它在打印行After segmentation fault
,Before free()
和After free()
上移动,但经过几次迭代后崩溃,始终在free()
(打印After segmentation fault
和Before free()
,但不是After free()
)。我想知道是什么导致了这种行为,以及什么是检测内存访问违规的最佳方式(我很惭愧,但我总是有点用printf
来确定程序崩溃的位置,但确定必须有更好的工具来做到这一点)它很难被检测到(大多数情况下,它不会在违规代码中崩溃,但是,例如,在后面的代码中,当试图再次使用该内存时)。当然,我应该能够释放这个内存,因为我正确地分配它并且不修改指针。
这是未定义的行为。顾名思义,“未定义行为”意味着任何事情都可能发生,没有崩溃保证,只是......任何事情。 – SirDarius
根据定义,未定义的行为是未定义的。它可以导致崩溃(分段错误(或其他)),它可以*看起来很好,或者它可能在一个完全不相关的地方崩溃,甚至导致[鼻恶魔](http://www.catb.org/)。行话/ HTML/N /鼻demons.html)。 –
它可能导致'free'问题的原因是某些分配算法(特别是在调试模式下)将数据存储在分配的内存之外。 –