C语言姓名列表程序排序异常求助:编号排序出现1、10、2...错误
问题分析与修复方案
核心问题根源
排序异常的本质是将编号以字符串形式存储并使用strcmp做字符串比较。字符串比较是按ASCII码逐字符对比的:"10"的第一个字符'1'(ASCII值49)小于"2"的第一个字符'2'(ASCII值50),因此strcmp("10", "2")会返回负数,程序判定"10"比"2"小,最终导致10排在2前面的错误顺序。
此外代码还存在多个潜在问题:
char num[2]长度不足:存储"10"需要至少3个字符位(两个数字+字符串终止符'\0'),当前定义会造成缓冲区溢出,引发未定义行为。gets(str)不安全:无法限制输入长度,极易导致缓冲区溢出。- 字符串未加终止符:手动分割编号、姓氏、名字时,未添加
'\0',导致后续strcmp、strlen等函数读取内存垃圾数据。 - 重复名字统计逻辑错误:原逻辑会重复统计部分重复名字,导致计数不准确。
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> struct people { int num; // 改为整数存储编号,彻底解决排序问题 char surname[20]; char name[10]; } peoples[10], c; int main() { int i, j, l = 0, m; float s = 0; char str[100]; char *token; system("chcp 1251 > nul"); for (i = 0; i < 10; i++) { printf("Enter number, surname, name %d of your human: ", i + 1); // 替换gets为fgets,避免缓冲区溢出 if (fgets(str, sizeof(str), stdin) == NULL) { break; } // 去除输入末尾的换行符 str[strcspn(str, "\n")] = '\0'; // 使用strtok分割输入字符串,比手动循环更可靠 token = strtok(str, " "); if (token != NULL) { peoples[i].num = atoi(token); // 字符串转整数 } token = strtok(NULL, " "); if (token != NULL) { strncpy(peoples[i].surname, token, sizeof(peoples[i].surname)-1); peoples[i].surname[sizeof(peoples[i].surname)-1] = '\0'; // 确保字符串终止符 } token = strtok(NULL, " "); if (token != NULL) { strncpy(peoples[i].name, token, sizeof(peoples[i].name)-1); peoples[i].name[sizeof(peoples[i].name)-1] = '\0'; } } // 修正不同名字统计逻辑:检查当前名字是否已在之前出现过 l = 0; for (i = 0; i < 10; i++) { m = 1; for (j = 0; j < i; j++) { if (!strcmp(peoples[i].name, peoples[j].name)) { m = 0; break; } } if (m == 1) { l++; } s += strlen(peoples[i].surname); } // 冒泡排序:按整数编号比较,逻辑简单直接 for (i = 0; i < 9; i++) { for (j = 0; j < 9 - i; j++) // 优化内层循环次数,减少冗余操作 { if (peoples[j].num > peoples[j+1].num) { c = peoples[j]; peoples[j] = peoples[j+1]; peoples[j+1] = c; } } } // 输出结果 for (i = 0; i < 10; i++) { printf("%d ", peoples[i].num); printf("%s ", peoples[i].name); printf("%s ", peoples[i].surname); printf("\n"); } printf("\nYou have %d different names\n", l); printf("Average length of surname is = %.2f\n", s / 10); return 0; }
关键修改说明
- 编号存储与排序:将编号改为整数类型存储,排序时直接比较整数大小,彻底解决字符串排序的异常问题。
- 输入安全优化:用
fgets替代gets,并去除换行符,避免缓冲区溢出风险。 - 字符串处理优化:使用
strtok分割输入,配合strncpy确保字符串不溢出,并手动添加终止符,避免垃圾数据干扰。 - 统计逻辑修正:改为检查当前名字是否在之前的条目里出现过,避免重复计数,统计结果更准确。
- 排序效率优化:调整冒泡排序的内层循环范围,减少不必要的比较操作。
内容的提问来源于stack exchange,提问作者Morkovka
相关产品推荐
相关产品推荐

