我正在尝试调试由其他人编写的一段代码,这些代码在memcpy操作期间有时会导致段错误,但不是所有时间。
另外,如果有人能帮我翻译memcpy之前发生的代码,我会非常感激。
首先,我们有一个函数,其中传递一个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中的char数组被转换为void指针,然后设置为src的值时,会发生什么?我无法追随该步骤应该发生的事情。
在此之后,memcpy发生了:
memcpy(dest, src, buf->bufSize*numStuff)
这就是经常发生段错误的地方。我已经检查了dest / src为null,都不是null。
此外,在调用ExampleFunction的函数中,如果重要,则声明dest的数组的大小为5000。但是,当我在上面的代码中打印buf-> bufSize * numStuff中的值时,该值通常高于5000 - 它可以高达80,000 - 尽管没有segfaulting。也就是说,它运行良好,长度变量(buf->; bufSize * numStuff)远远高于dest变量初始化的假设长度。但是,也许这无关紧要,因为它被转换为无效指针?
由于各种原因,我无法使用dbg或安装IDE。我只是使用基本的printf调试。有没有人有我可以探索的想法?提前谢谢。
答案 0 :(得分:0)
首先,强制转换和赋值只是将buf->stuff
的地址复制到指针src
中。那里没有魔力。
numStuff = buf->validStuff;
src = (void *)(buf->stuff);
如果dest
只有5000
个字节的足够存储空间,并且您尝试写入超过该长度的存储空间,那么您会损坏您的程序堆栈,这会导致在副本上或有时稍晚的段错误。无论你是否转换为无效指针都没有任何区别。
memcpy(dest, src, buf->bufSize*numStuff)
我认为你需要确切地知道buf->bufSize*numStuff
应该计算什么,如果它不正确(不打算),要么修复它,要么将副本截断到目标的大小,要么增加目标数组。
答案 1 :(得分:0)
空指针解除引用不是导致段错误的唯一因素。当程序分配内存时,当您尝试访问 已分配的内存区域后,也可能触发段错误。
您的代码看起来打算将buf-> stuff指向的缓冲区的内容复制到目标缓冲区。如果这些缓冲区中的任何一个小于memcpy操作的大小,则memcpy可能会超出已分配内存的范围并触发段错误。
因为内存分配器以大块分配内存,然后将其分解为对malloc的各种调用,所以每次运行malloc的缓冲区结束时,代码都不会一直失败。您将完全了解您描述的零星故障行为。
此代码中的假设是buf-> stuff指向的缓冲区和dest指针的长度至少为“buf-> bufSize * numStuff”字节。这两个假设中的一个是错误的。
我建议采用几种方法:
检查分配dest指向的缓冲区和buf-> stuff指向的缓冲区的代码,并确保它们总是大于或大于buf-> bufSize * numStuff。
如果做不到这一点,有很多工具可以帮助您从程序中获得更好的诊断信息。最简单的用法是efence(“电围栏”),它有助于识别代码中超出任何缓冲区的位置。 (http://linux.die.net/man/3/efence)。使用valgrind(http://valgrind.org/)可以进行更彻底的分析 - 但是Valgrind使用起来要多得多。
PS。将一个char *指针转换为void *指针没什么特别的 - 它仍然只是一个已分配的内存块的地址。