You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 编号存储与排序:将编号改为整数类型存储,排序时直接比较整数大小,彻底解决字符串排序的异常问题。
  2. 输入安全优化:用fgets替代gets,并去除换行符,避免缓冲区溢出风险。
  3. 字符串处理优化:使用strtok分割输入,配合strncpy确保字符串不溢出,并手动添加终止符,避免垃圾数据干扰。
  4. 统计逻辑修正:改为检查当前名字是否在之前的条目里出现过,避免重复计数,统计结果更准确。
  5. 排序效率优化:调整冒泡排序的内层循环范围,减少不必要的比较操作。

内容的提问来源于stack exchange,提问作者Morkovka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 10:20:42