我正在尝试调试一段由其他人编写的代码,该代码有时会导致段错误,但不是所有时间,在内存操作期间。
另外,如果有人能帮助我翻译内存内存之前的一段代码,我将非常感激。
首先,我们有一个函数,其中传递了一个void指针和一个指向结构体的指针,如下所示:
void ExampleFunction(void *dest, StuffStruct *buf)
结构体看起来像这样:
typedef struct {
char *stuff;
unsigned int totalStuff;
unsigned int stuffSize;
unsigned int validStuff;
} StuffStruct;
返回到ExampleFunction。在ExampleFunction内部,这是发生的:
void *src;
int numStuff;
numStuff = buf->validStuff;
src = (void *)(buf->stuff);
我被上面的行弄糊涂了。当buf->stuff中的字符数组被转换为空指针,然后设置为src的值时,究竟会发生什么?我不知道这一步应该怎么走。
在此之后,内存发生:
memcpy(dest, src, buf->bufSize*numStuff)
这就是段故障经常发生的地方。我已经检查了dest/src为空,也不是空的。
另外,在调用ExampleFunction的函数中,dest的数组被声明为5000的大小,如果有必要的话。然而,当我在上面的代码中打印buf->bufSize*numStuff中的值时,这个值通常高于5000——它可以高达80000——但是没有出现分段错误。也就是说,当长度变量(但是->bufSize*numStuff)远高于dest变量初始化时的假定长度时,它可以正常运行。然而,也许这并不重要,因为它被转换为一个空指针?
由于各种原因,我无法使用dbg或安装IDE。我只是在使用基本的打印调试。有人能给我提点建议吗?
首先,强制转换和赋值操作只是将buf->stuff
的地址复制到指针src
中。这里没有魔法。
numStuff = buf->validStuff;
src = (void *)(buf->stuff);
如果dest
只有足够的5000
字节的存储空间,并且您试图写入超出该长度的内容,那么您正在损坏程序堆栈,这可能导致在副本上或有时稍后出现段故障。是否强制转换为void指针没有任何区别。
memcpy(dest, src, buf->bufSize*numStuff)
我认为你需要弄清楚到底什么buf->bufSize*numStuff
应该是计算,要么修复它,如果它是不正确的(不打算),截断复制到目标的大小,或增加目标数组的大小。
空指针解引用并不是导致段错误的唯一原因。当你的程序分配内存时,当你试图访问在你分配的内存区域之后的内存时,也有可能触发段错误。
您的代码看起来像是打算将buf->stuff指向的缓冲区的内容复制到目标缓冲区。如果这些缓冲区中的任何一个小于memcpy操作的大小,则memcpy可能会超出已分配内存的界限并触发段错误。
因为内存分配器在大块中分配内存,然后将其分配给对malloc的各种调用,所以您的代码不会在每次运行超过malloc缓冲区的末尾时始终失败。您将得到您所描述的零星故障行为。
这段代码中的假设是,buf->stuff指向的缓冲区和dest指针指向的缓冲区的长度至少为"buf->bufSize * numStuff"字节。这两个假设有一个是错误的。
我建议以下几种方法:
-
检查分配由dest指向的缓冲区和由buf->stuff指向的缓冲区的代码,并确保它们总是等于或大于buf->bufSize * numStuff。
-
如果做不到这一点,有很多工具可以帮助您从程序中获得更好的诊断信息。使用最简单的是篱笆("Electric Fence"),它将帮助识别代码中超出缓冲区的地方。(http://linux.die.net/man/3/efence)。可以使用valgrind (http://valgrind.org/)进行更彻底的分析,但是valgrind使用起来有点复杂。
祝你好运!
p。将char*指针转换为void*指针没有什么特别之处——它仍然只是一个分配的内存块的地址。