Valgrind 警告:我应该认真对待吗

Posted

技术标签:

【中文标题】Valgrind 警告:我应该认真对待吗【英文标题】:Valgrind Warning: Should I Take It Seriously 【发布时间】:2011-06-16 22:58:14 【问题描述】:

背景: 我有一个模仿fgets(character, 2, fp) 的小例程,除了它从字符串而不是流中获取字符。 newBuff 是动态分配的字符串,作为参数传递,字符声明为char character[2]

例行公事:

character[0] = newBuff[0];

character[1] = '\0';

strcpy(newBuff, newBuff+1);

strcpy 在读取每个字符时复制信息的丢失。

问题:Valgrind 确实警告过我 这个活动,“来源和目的地 strcpy 中的重叠(0x419b818, 0x419b819)"。

我应该担心这个警告吗?

【问题讨论】:

【参考方案1】:

答案是肯定的:对于某些编译器/库实现,我猜是最新的,你最终会得到一个虚假的结果。示例见How is strcpy implemented?。

【讨论】:

【参考方案2】:

如果源和目标重叠,strcpy() 的行为是官方未定义的。

来自 memcpy 的手册页有一个建议:

memcpy() 函数将 n 个字节从内存区 s2 复制到内存区 s1。如果 s1 和 s2 重叠,则行为未定义。 s1 和 s2 可能重叠的应用程序应该使用 memmove(3) 代替。

【讨论】:

不管它是如何实现的,行为都是未定义的。这在标准中有明确规定。 strcpy 如果缓冲区重叠,则会给出未定义的行为。 memcpy 做同样的事情,但在其他方面无关紧要。 好的,我本地的 strcpy 手册页不包含该警告,但 memcpy 包含。 C 标准中接受指向重叠内存区域的指针进行读写的唯一函数是memmovewmemmove。对于其他所有内容(包括snprintf!),行为未定义。【参考方案3】:

可能标准没有指定这些缓冲区重叠时会发生什么。所以是的,valgrind 对此进行投诉是对的。

实际上,您很可能会发现您的 strcpy 按从左到右的顺序复制(例如while (*dst++ = *src++);),这不是问题。但它仍然不正确,并且在与其他 C 库一起运行时可能会出现问题。

一种符合标准的写法是:

memmove(newBuff, newBuff+1, strlen(newBuff));

因为memmove 被定义为处理重叠。 (虽然在这里你最终会遍历字符串两次,一次检查长度,一次复制。我也采取了捷径,因为strlen(newBuff) 应该等于strlen(newBuff+1)+1,这是我最初写的。)

【讨论】:

即使顺序是从左到右的,由于展开/重新排序和大于字节的复制单元也可能会出现问题。我认为strcpy 的这种用法非常不安全,并且可能在同一库的不同版本之间甚至在使用不同编译器的不同构建之间实现平衡。 @R.. 我同意,尤其是关于大于字节单位的观点。为了清楚起见,我同意代码不正确,应该更改。【参考方案4】:

是的,您还应该担心您的函数的性能非常糟糕(O(n^2) 表示应该是 O(n) 的任务)。每次读取一个字符时将字符串的全部内容向后移动一个字符是非常浪费时间的。相反,您应该只保留一个指向当前位置的指针并增加该指针。

您发现自己需要memmove 或等效项(在重叠的缓冲区之间复制)的情况几乎总是表明存在设计缺陷。通常这不仅仅是实现中的缺陷,而是界面中的缺陷。

【讨论】:

【参考方案5】:

是的,你应该担心。 C 标准规定,当源对象和目标对象重叠时,strcpy 的行为是undefined。未定义的行为意味着它有时可能会起作用,或者可能会失败,或者它可能看起来成功但在程序的其他地方明显失败。

【讨论】:

【参考方案6】:

是的——strcpy 的行为仅在源和目标不重叠时定义。您可以考虑将strlenmemmove 组合使用。

【讨论】:

以上是关于Valgrind 警告:我应该认真对待吗的主要内容,如果未能解决你的问题,请参考以下文章

内存检测工具Valgrind

如何根据 Valgrind 输出进一步调试

Unix下C程序内存泄漏检测工具Valgrind安装与使用

既然喜欢就要认真对待

记得很早很早之前有过一次面试,面试前端说自己喜欢JavaScript,然后面试官问,你知道当前JavaScript最新标准和规范吗?我无言以对,因为平时没有关注认真对待这些信息,然后就没有然后了。

毕设开题