背景:
我有一个模仿fgets(character, 2, fp)
的小例程,除了它从字符串而不是流中获取字符。 newBuff是动态分配的字符串,作为参数传递,字符声明为char character[2]
。
例程:
character[0] = newBuff[0];
character[1] = '\0';
strcpy(newBuff, newBuff+1);
strcpy在从中读取每个字符时复制信息丢失。
问题:Valgrind警告我 这项活动,“来源和目的地 strcpy重叠(0x419b818, 0x419b819)”。
我应该担心这个警告吗?
答案 0 :(得分:11)
可能标准没有规定当这些缓冲区重叠时会发生什么。是的,valgrind
抱怨这是正确的。
实际上,您很可能会从左到右(例如strcpy
)按顺序找到您的 while (*dst++ = *src++);
副本,并且这不是问题。但它仍然不正确,并且在与其他C库一起运行时可能会出现问题。
一种标准正确的写法方式是:
memmove(newBuff, newBuff+1, strlen(newBuff));
因为memmove
被定义为处理重叠。 (虽然在这里你最终会遍历字符串两次,一次检查长度和一次复制。我也采用了一个快捷方式,因为strlen(newBuff)
应该等于strlen(newBuff+1)+1
,这是我最初写的。)
答案 1 :(得分:5)
是的,您还应该担心您的函数在性能方面有不良影响(O(n^2)
应该是O(n)
的任务。每次读取一个角色时,都会通过角色移回字符串的全部内容,这是一个巨大的浪费时间。相反,你应该只保留一个指向当前位置的指针并增加该指针。
您发现自己需要memmove
或等效的情况(在重叠的缓冲区之间复制)几乎总是表示设计缺陷。通常,它不仅仅是实现中的缺陷,而是在界面。
答案 2 :(得分:4)
是 - 仅在源和目标不重叠时才定义strcpy
的行为。您可以考虑使用strlen
和memmove
的组合。
答案 3 :(得分:4)
是的,你应该担心。 C标准声明当源和目标对象重叠时strcpy
的行为为undefined。未定义的行为意味着它可能有时会起作用,或者它可能会失败,或者它可能看起来成功但会在程序的其他地方出现失败。
答案 4 :(得分:3)
如果源和目标重叠,则strcpy()
的行为正式未定义。
来自memcpy的联机帮助页提出了一个建议:
memcpy()函数将n个字节从存储区s2复制到存储区s1。如果s1和s2重叠,则行为未定义。 s1和s2可能重叠的应用程序应使用memmove(3)代替。
答案 5 :(得分:2)
答案是肯定的:对于某些编译器/库实现,我猜最新的,你最终会得到一个虚假的结果。有关示例,请参阅How is strcpy implemented?。