Clang 13 -O2优化输出异常而GCC正常,代码是否存在未定义行为?
问题根因
你的代码存在明确的未定义行为(UB),问题出在string_push_char函数中:
void string_push_char(String *self, char c) { self->len = self->len + 1; char *temp = realloc(self->s, self->len); // realloc返回新内存指针 if (temp == NULL) { printf("Failed to allocate memory in \"string_push_char()\".\n"); exit(-1); } // !!!这里漏掉了将新指针赋值回self->s的操作:self->s = temp; self->s[self->len - 2] = c; // 仍然使用已经被realloc释放的旧指针写入 self->s[self->len - 1] = 0; }
realloc在调整内存大小时,如果原有内存块后面没有足够的连续空间,会申请一块新的内存、拷贝原有数据、释放旧内存块,最终返回新的内存地址。你没有把新地址赋值给self->s,后续写入操作都是针对已经被释放的旧内存地址,属于**释放后使用(Use After Free)**错误。
不同编译环境表现不一致的原因
未定义行为的典型特征就是跨编译器、跨优化级别表现不稳定:
- 低优化级别、GCC各优化级别下,
realloc刚好没有移动内存块,旧指针仍然指向有效的内存地址,写入操作刚好生效,因此输出符合预期。 - Clang开启-O2优化时,编译器会做激进的优化,
realloc调用后旧指针会被标记为无效,你对旧指针的写入操作不会同步到新分配的内存块中,新内存里还是初始的空字符串,因此输出为: 5。 - 加入
-fsanitize=undefined参数后, sanitizer会替换标准库的内存分配实现,realloc的行为逻辑发生变化,不会激进地标记旧指针为无效,因此运行结果又符合预期。
问题排查方法
- 首先开启编译告警:编译时加入
-Wall -Wextra参数,Clang、GCC都会直接提示你temp变量定义后未使用,直接指向问题代码。 - 使用内存检测工具:比如Valgrind、AddressSanitizer(编译加
-fsanitize=address),运行程序会直接报出Use After Free错误,定位到出错的代码行。 - 遇到优化级别改变就结果异常的场景,优先排查内存操作相关的未定义行为:包括指针越界、释放后使用、未初始化变量访问等。
修复方案
只需要在realloc调用成功后,将新指针赋值回self->s即可:
void string_push_char(String *self, char c) { self->len = self->len + 1; char *temp = realloc(self->s, self->len); if (temp == NULL) { printf("Failed to allocate memory in \"string_push_char()\".\n"); exit(-1); } self->s = temp; // 新增这行 self->s[self->len - 2] = c; self->s[self->len - 1] = 0; }
内容的提问来源于stack exchange,提问作者C-W-M-Oliver8012
相关产品推荐
相关产品推荐

