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

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

常规:

 character[0] = newBuff[0]; character[1] = '\0'; strcpy(newBuff, newBuff+1); 

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

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

我应该担心这个警告吗?

可能标准没有规定当这些缓冲区重叠时会发生什么。 所以,是的, valgrind对此抱怨是对的。

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

一种标准正确的写法是:

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

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

是的,您还应该担心您的function在病态上有不良的性能(对于应该为O(n)的任务, O(n^2) )。 每次阅读角色时,都会通过角色移回字符串的全部内容,这是一个巨大的浪费时间。 相反,你应该只保留一个指向当前位置的指针并增加该指针。

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

是 – 仅在源和dest不重叠时才定义strcpy的行为。 您可以考虑使用strlenmemmove的组合。

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

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

来自memcpy的手册页提出了一个建议:

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

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