6

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

常规:

character[0] = newBuff[0];

character[1] = '\0';

strcpy(newBuff, newBuff+1);

strcpy 复制了从其中读取每个字符时丢失的信息。

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

我应该担心这个警告吗?

4

6 回答 6

11

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

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

一种标准正确的编写方法是:

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

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

于 2011-01-28T00:54:16.440 回答
5

是的,而且您还应该担心您的功能表现不佳(O(n^2)对于本应如此的任务O(n))。每次读取一个字符时将字符串的全部内容向后移动一个字符是非常浪费时间的。相反,您应该只保留一个指向当前位置的指针并增加该指针。

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

于 2011-01-28T00:57:16.043 回答
4

是的——strcpy只有在源和目标不重叠时才定义的行为。您可以考虑使用strlenand的组合memmove

于 2011-01-28T00:53:18.140 回答
4

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

于 2011-01-28T00:53:18.373 回答
3

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

从 memcpy 的手册页中提出了一个建议:

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

于 2011-01-28T00:53:15.893 回答
2

答案是肯定的:对于某些编译器/库实现,我猜是最新的,你最终会得到一个虚假的结果。请参阅strcpy 是如何实现的?例如。

于 2012-12-09T13:48:13.390 回答