不久前,我在我写的某个大数字图书馆里发现了一个bug,花了我不少时间。问题是,我违反了某个结构成员的内存限制,但它没有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
来确定程序崩溃的位置,但肯定有更好的工具可以做到这一点),因为很难检测到(大多数情况下,它不会在违反代码时崩溃,但是,就像在示例中一样,在代码,当再次尝试使用此内存时)。当然,我应该能够释放这个内存,因为我分配正确,没有修改指针。 最佳答案
当你在记忆中读或写的时候,你没有自己的记忆,你会有不确定的行为。
这并不总是导致分割错误。实际上,代码很可能会损坏其他一些数据,并且程序会在其他地方崩溃,这使得调试变得很困难。
在本例中,您写入的堆地址无效。很可能您会损坏一些内部堆结构,这使得程序很可能在随后的malloc或free调用上崩溃。
有一些工具可以检查堆的使用情况,并告诉您是否超出了界限。我喜欢并推荐valgrind for linux和gflags for windows。