不同GCC版本输出异常:count变量无法正常累加的排查与修复
问题重现
你遇到的这个版本差异问题很典型:同样的代码在GCC 5.4.1上运行完全正常,但在GCC 9.3环境中,每次循环都会把count变量意外重置为0。先回顾一下你提供的异常运行输出:
count:0
DNS cache empty.
Do you want to add/modify the DNS table(y/n)?y
Domain name: yahoo.com
IP address: 10.2.45.67SERVER NAME IP ADDRESS yahoo.com 10.2.45.67Do you want to add/modify the DNS table?(y/n)y
count:0
DNS cache empty.
Do you want to add/modify the DNS table(y/n)?n
根本原因:栈内存越界覆盖
问题的核心出在你读取用户选择的scanf调用上:
scanf("%s", &choice);
choice是一个单个char类型变量,但你误用了%s格式符——这个格式符是用来读取字符串的,它会把用户输入的字符(包括回车后的换行符)持续写入到&choice指向的内存地址,直到遇到空白字符,并且会自动在末尾添加\0作为字符串结束符。
因为choice只占用1字节的栈空间,多出来的\0就会溢出到相邻的栈内存区域。刚好在GCC 9.3的栈布局中,count变量的内存位置紧挨着choice的后面,所以每次执行这个scanf,count就被意外覆盖成0了。
而GCC 5.4.1的栈布局可能把count放在了离choice更远的位置,或者栈对齐规则不同,刚好没触发这个覆盖问题,但这只是巧合——你的代码本身存在严重的内存越界bug,在任何环境下都可能引发未知问题。
修复方案
把所有读取choice的scanf调用改成读取单个字符的格式,同时处理输入流中残留的空白字符(比如之前输入域名/IP后留下的换行):
// 替换原来的scanf("%s", &choice); scanf(" %c", &choice);
这里的两个关键调整:
%c是专门读取单个字符的格式符,不会写入额外的\0,彻底避免内存越界- 前面的空格
" %c"会自动跳过输入流中的所有空白字符(空格、换行、制表符等),避免读取到之前输入操作留下的无效换行符
另外,你的代码还有一个小逻辑bug:当修改已有域名的IP时,原来的代码会执行count +=1,这会导致count错误累加(实际条目数没增加,但计数器变大),所以需要在修改分支里抵消这个累加:
修改后的核心代码片段:
int main(int argc, char **argv){ int count = 0; char choice; char domain_name[50]; char ip_address[50]; int i; do { printf("count:%d\n",count); if(count == 0) { printf("DNS cache empty.\n"); printf("Do you want to add/modify the DNS table(y/n)?"); scanf(" %c", &choice); // 修复读取choice的方式 if(choice == 'n') { break; } } printf("Domain name: "); scanf("%s",domain_name); for(i=0; i<20; i++) { if(strcmp(domain_name,table[i].server_name) == 0 ){ break; } } ip: strcpy(ip_address, " "); printf("IP address: "); scanf("%s", ip_address); if(validate_ip(ip_address) == 0){ goto ip; } if(i == 20){ strcpy(table[count].server_name,domain_name); strcpy(table[count].ip,ip_address); } else { strcpy(table[i].ip,ip_address); count -= 1; // 抵消后续的count+=1,避免计数器错误增加 } count+=1; printDnsTable(count); choice = ' '; printf("\nDo you want to add/modify the DNS table?(y/n)"); scanf(" %c", &choice); // 修复读取choice的方式 } while(choice == 'y'); }
这样修改后,代码彻底解决了内存越界问题,同时修正了计数器的逻辑错误,在GCC 9.3以及其他任何GCC版本上都能稳定运行。
内容的提问来源于stack exchange,提问作者Mia

