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
的行为。 您可以考虑使用strlen
和memmove
的组合。
是的,你应该担心。 C标准声明当源和目标对象重叠时, strcpy
的行为是未定义的。 未定义的行为意味着它可能有时会起作用,或者可能会失败,或者它可能看起来成功但会在程序的其他地方显示失败。
如果源和目标重叠,则strcpy()
的行为正式未定义。
来自memcpy的手册页提出了一个建议:
memcpy()函数将n个字节从存储区s2复制到存储区s1。 如果s1和s2重叠,则行为未定义。 s1和s2可能重叠的应用程序应使用memmove(3)。
答案是肯定的:对于某些编译器/库实现,我猜最新的,你最终会得到一个虚假的结果。 请参阅如何实现strcpy? 举个例子。