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

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

character[1] = '\0';

strcpy(newBuff, newBuff+1);

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

问题:Valgrind警告我这种活动,“源和目标在strcpy(0x419b818, 0x419b819)中重叠”。

我应该担心这个警告吗?

6个回答

12

标准可能没有规定这些缓冲区重叠时会发生什么。所以,是的,valgrind 的投诉是正确的。

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

编写此代码的一种符合标准的方法是:

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

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


即使顺序是从左到右的,由于展开/重新排序和大于字节单位的复制,可能会出现问题。我认为这种使用strcpy非常不安全,很可能在同一库的不同版本之间甚至不同编译器的不同构建之间发生故障。 - R.. GitHub STOP HELPING ICE
@R.. 我同意,尤其是关于大于字节单位的观点。只是为了明确,我同意代码是不正确的,应该进行更改。 - asveikau
我认为你最初写的 strlen(newBuff+1)+1 更好。它应该出现在你的代码中,因为它易读。它也应该在Stackoverflow上出现,以避免混淆。 - Daniel Chin

5
是的,你还应该担心你的函数性能极差(对于一个应该是O(n)的任务却是O(n^2))。每次读取一个字符时将整个字符串的内容向后移动一个字符是非常浪费时间的。相反,你应该只保留当前位置的指针并递增该指针。
需要使用memmove或等效函数(在重叠缓冲区之间复制)的情况几乎总是表示设计存在缺陷。通常不仅是实现上的缺陷,而且还涉及接口方面的缺陷。

4
是的 - 只有在源和目标不重叠时,strcpy 的行为才被定义。您可以考虑使用 strlenmemmove 的组合来替代。

4

是的,你应该担心。C标准规定,当源对象和目标对象重叠时,strcpy的行为是未定义的。未定义的行为意味着它有时可能工作,或者它可能失败,或者它可能表面上成功但在程序的其他地方表现为失败。


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

从memcpy的man页中得出一个建议:

memcpy()函数将n个字节从内存区域s2复制到内存区域s1。如果s1和s2重叠,则行为是未定义的。应用程序中可能存在s1和s2重叠的情况,应改用memmove(3)。


1
无论如何实现,行为都是未定义的。这在标准中明确说明了。 - R.. GitHub STOP HELPING ICE
如果缓冲区重叠,strcpy会产生不确定的行为。memcpy也会产生同样的结果,但它在其他方面是无关紧要的。 - Jerry Coffin
好的,我的本地strcpy手册没有包含那个警告,但是memcpy手册有。 - Matt K
1
C标准中唯一接受读写重叠内存区域指针的函数是memmovewmemmove。对于其他所有函数(包括snprintf!),行为都是未定义的。 - R.. GitHub STOP HELPING ICE

2
答案是肯定的:使用某些编译器/库实现,最新的那些,你最终会得到一个虚假的结果。参见如何实现strcpy?中的示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接