提问人:tueda 提问时间:11/2/2023 最后编辑:Charles Duffytueda 更新时间:11/2/2023 访问量:340
“*s = 0”被优化掉。可能的 GCC 13 错误?还是一些未定义的行为?
"*s = 0" being optimized out. Possible GCC 13 bug? Or some undefined behaviour?
问:
在 GCC 13.2 中,以下代码的输出取决于优化级别:
#include <ctype.h>
#include <stdio.h>
char *SkipAName(char *s) {
if (('A' <= *s && *s <= 'Z') || ('a' <= *s && *s <= 'z') || *s == '_' ||
*s == '$') {
if (*s == '$') {
s++;
}
while (isalnum(*s)) {
s++;
}
if (*s == '_') {
s++;
}
}
return s;
}
int TestName(char *name) {
while (*name) {
name++;
}
return 0;
}
int StrICmp(char *s1, char *s2) {
while (*s1 && tolower(*s1) == tolower(*s2)) {
s1++;
s2++;
}
return tolower(*s1) - tolower(*s2);
}
int DoTable(char *s) {
char *name, c;
do {
name = s;
s = SkipAName(s);
c = *s;
*s = 0;
TestName(name);
*s = c;
if (*s == '(') {
break;
}
if (*s != ',') {
printf("Error 1\n");
return 1;
}
*s = 0;
if (StrICmp(name, "sparse") == 0) {
} else {
printf("Error 2\n");
return 1;
}
*s++ = ',';
while (*s == ',') {
s++;
}
} while (*s);
printf("OK\n");
return 0;
}
int main() {
char buf[] = "sparse,C(1)";
DoTable(buf);
return 0;
}
$ gcc-13 -O0 test.c && ./a.out
OK
$ gcc-13 -O1 test.c && ./a.out
OK
$ gcc-13 -O2 test.c && ./a.out
Error 2
$ gcc-13 -O3 test.c && ./a.out
Error 2
代码来自这个项目,我试图做一个最小的可重现的例子;这就是为什么这段代码可能看起来很尴尬。另请参阅此问题。
我想知道这是否是 GCC 13 中的错误,或者我是否遇到了一些未定义的行为。使用 Compiler Explorer,看起来第 52 行以某种方式进行了优化。该代码适用于 GCC 12.3。*s = 0
答:
我想知道这是否是 GCC 13 中的错误,或者我是否遇到了一些未定义的行为。
和的参数应该是已转换为
无符号字符
类型的 s(在自动转换为以匹配参数类型之前)。例:。该问题可能会导致类似的程序显示 UB,但示例中特定程序提供的输入不会产生这种效果。isalnum()
tolower()
char
int
isalnum((unsigned char) *s)
该代码假定大写拉丁字母在执行字符集中编码为连续的数字范围,小写拉丁字母也是如此。C 不保证会是这样,如果不是这样,那么程序将无法按预期工作。但是,在测试环境中,该问题根本不可能成为您的问题。
SkipAName()
对于它认为要跳过的名称,似乎也有奇怪的规则,但这并没有错。TestName()
没有调用方可观察的效果。但这并不是错误的,如果观察到的不当行为取决于该函数以及程序中存在的对它的调用,那么这非常奇怪。StrICmp()
可以从第二个字符串的末尾运行,从而产生 UB,但这不会发生在您的特定输入中。修改其输入有点令人讨厌,尤其是它不会始终如一地逆转其更改,因为它们似乎只是为了服务于其内部目的。除其他外,这使得与字符串文字一起使用是不安全的。但是您的示例输入不是字符串文字,并且修改它本身并不是错误的。
DoTable()
DoTable()
我不喜欢使用参数。我并不完全反对函数修改其参数,但我非常赞成使用描述性名称。目前还不清楚“”这个名字的含义是什么,它既有充分的描述性,又与它的所有用途一致。但是,当然,这并不意味着代码是错误的。
DoTable()
s
s
总的来说,这段代码有一些问题,我总体上不喜欢它,但我没有看到它呈现的未定义行为。在更高的优化级别下,您为 GCC 13 版本显示的输出是错误的。也就是说,它们反映了 GCC 13 中的一个错误。
评论
*s
评论
*s = 0;
s = SkipAName(s);
*s = 0
StrICmp
mov BYTE PTR [rbp+0], 0