*s = 0被优化消除:是GCC 13的Bug还是触发了未定义行为?
GCC 13优化级导致代码行为异常:未定义行为还是编译器Bug?
在GCC 13.2版本中,以下C代码的输出结果取决于编译时的优化等级:
#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
该代码源自form项目,已精简为最小复现示例。通过Compiler Explorer可观察到,第52行的*s = 0在O2/O3优化级下被优化消除。该代码在GCC 12.3中可正常运行,现咨询此现象是GCC 13的Bug还是触发了C语言的未定义行为?
这一现象是代码触发了C语言的未定义行为,而非GCC 13的Bug,具体原因如下:
ctype函数参数违规
<ctype.h>中的isalnum、tolower等函数明确要求:参数必须是unsigned char转换为int的值,或者EOF。如果直接传递char类型参数,当char为有符号类型且字符值大于127时,会被解析为负数,违反参数要求,触发未定义行为。优化器的激进处理
即使当前测试用例使用的是ASCII范围内的非负字符,C标准允许编译器对存在未定义行为的程序进行任意优化。GCC 13在O2/O3优化级下,基于代码中潜在的未定义行为假设,进行了激进优化,错误消除了*s = 0的操作,导致StrICmp比较时,name指向的字符串未被正确截断为"sparse",最终返回不相等的结果。旧版本编译器的巧合正常
GCC 12.3未出现问题只是旧版本优化策略未针对该未定义行为做激进优化,并不代表代码本身合法。
修复方案
将传递给ctype函数的char参数强制转换为unsigned char:
- 把
isalnum(*s)改为isalnum((unsigned char)*s) - 把
tolower(*s1)、tolower(*s2)分别改为tolower((unsigned char)*s1)、tolower((unsigned char)*s2)
内容的提问来源于stack exchange,提问作者tueda
相关产品推荐
相关产品推荐

