我使用 strcmp 来比较多个字符串,通常每个程序运行大约数百或数千次比较。
我注意到,当我知道字符串完全相同时,strcmp 有时会返回非零。这种情况每隔几次运行就会发生一次,而且是不可预测的。很困惑,我使用 GDB 在 strcmp 的返回值非零时设置了一个断点,这样我就可以在调用 strcmp 后立即手动比较字符串。果然,它到达了断点,我在 GDB 中手动打印出了这两个字符串。它们完全相同,包括空终止字符。
更困惑的是,我将代码修改为以下内容,尝试通过在完全相同的字符串上运行两次 strcmp 来查明问题。在这种情况下,我知道
name
参数应始终匹配 R.ecos[0].name
:
Eco *get_eco(const char *name)
{
int r1 = strcmp(R.ecos[0].name, name);
int r2 = strcmp(R.ecos[0].name, name);
if (r1) {
//break here with GDB
} else {
return &R.ecos[0];
}
return 0;
}
我再次运行该程序几次以尝试产生错误。当断点命中时,我打印 r1 和 r2 并看到 r1 = 1 和 r2 = 0。
我的经验使我不相信编译器错误或 strcmp 本身存在错误的可能性。这段代码是游戏引擎的一部分,因此其他线程正在运行 FMOD(音频)和 GLFW3(输入),但这些都没有触及这里的内存,所以我没有理由认为多线程/存在一些可疑的情况竞争条件。还有什么会导致如此奇怪的错误?
我在Windows上使用mingw32,用gcc编译,并且我尝试了两个不同版本的gcc和gdb,都产生相同的结果。这在我的 Linux 版本上没有发生,尽管由于该错误的偶发性,我无法确定它不存在于 Linux 上,但是在该操作系统上运行数十次后我无法重现它。
编译器标志:
-static -ggdb -g -O0 -std=c11 -Wall -Wextra -pedantic -Wshadow -Wpointer-arith \
-Wcast-align -Wwrite-strings -Wmissing-prototypes \
-Wmissing-declarations -Wredundant-decls -Wnested-externs \
-Winline -Wno-long-long -Wuninitialized \
-Wstrict-prototypes
编辑:我也在 Linux 上使用 ASAN 运行该程序,但没有遇到错误
编辑2:在两个字符串的地址上设置硬件观察点后,例如
watch -l R.ecos[0].name[32]
我仍然在代码中命中断点,根本没有触发硬件观察点。
我的经验使我不相信编译器错误或 strcmp 本身存在错误的可能性。
GLIBC strstr
很久以前曾经有一个
bug,这样的bug有时确实存在。但你是对的,这不太可能。
我建议在 strcmp
之前制作字符串的
local副本。这应该可以提供关于
strcmp
中是否存在数据争用或错误的明确答案。
如果有合理的长度限制,这相当容易做到:
Eco *get_eco(const char *name)
{
char s1[MAXLEN], s2[MAXLEN];
strcpy(s1, R.ecos[0].name);
strcpy(s2, name);
int r = strcmp(s1, s2);
if (r) {
// break here, examine s1, s2, name and R.ecos[0].name
} else ...