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

[C]基于结构体与文件实现歌手数据增删改及相关问题咨询

嘿,咱们一步步来解决你这个歌手数据管理程序的问题,先把核心痛点拆解清楚:

一、数组扩容的解决方案

你现在用的是固定大小为21的数组,要实现动态扩容,C语言里最常用的就是动态内存分配+realloc,这比预分配超大数组要高效得多。

1. 先定义歌手结构体

首先把你的数据结构明确下来:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义歌手结构体
typedef struct {
    char first_name[50];  // 姓名
    char last_name[50];   // 姓氏
    int id;               // 唯一ID
    char genre[30];       // 流派
} Singer;

2. 动态数组初始化与扩容逻辑

用指针代替固定数组,初始分配21个元素的空间,当存储数量达到当前容量时,自动扩容(比如每次扩容一倍,或者固定增加20个,按需调整):

int main() {
    // 初始化动态数组:初始容量21
    Singer *singers = malloc(21 * sizeof(Singer));
    int current_capacity = 21;
    int singer_count = 0;  // 当前已存储的歌手数量

    // 当需要添加新歌手时,先检查是否已满
    if (singer_count == current_capacity) {
        // 扩容:这里选择容量翻倍,你也可以改成 current_capacity += 20
        current_capacity *= 2;
        Singer *temp_ptr = realloc(singers, current_capacity * sizeof(Singer));
        
        // 必须检查realloc是否成功(内存不足时会返回NULL)
        if (temp_ptr != NULL) {
            singers = temp_ptr;
            printf("数组已扩容,当前容量:%d\n", current_capacity);
        } else {
            printf("内存分配失败,无法添加新歌手!\n");
            // 这里可以选择退出或者做降级处理
            return 1;
        }
    }

    // 后续增删改逻辑...

    // 程序退出前一定要释放动态内存,避免泄漏
    free(singers);
    return 0;
}
二、核心功能实现思路

1. Case 2:屏幕展示数据

直接遍历已存储的歌手数据即可,注意只遍历singer_count个元素(不要遍历整个容量):

void display_singers(Singer *singers, int count) {
    if (count == 0) {
        printf("暂无歌手数据!\n");
        return;
    }

    printf("\n===== 歌手列表 =====\n");
    printf("ID\t姓名\t\t姓氏\t\t流派\n");
    for (int i = 0; i < count; i++) {
        printf("%d\t%-10s\t%-10s\t%s\n", 
               singers[i].id, 
               singers[i].first_name, 
               singers[i].last_name, 
               singers[i].genre);
    }
}

2. Case 1:结构体数据写入文件

推荐用二进制写入(保留结构体的原始格式,读取更方便),如果需要可读的文本格式也可以用文本写入:

// 二进制写入(推荐)
void save_to_file(Singer *singers, int count) {
    FILE *file = fopen("singers.dat", "wb");
    if (!file) {
        printf("无法打开文件进行写入!\n");
        return;
    }

    // 写入所有已存储的歌手数据
    fwrite(singers, sizeof(Singer), count, file);
    fclose(file);
    printf("数据已成功保存到文件!\n");
}

// 可选:文本格式写入(方便手动查看)
void save_to_text_file(Singer *singers, int count) {
    FILE *file = fopen("singers.txt", "w");
    if (!file) {
        printf("无法打开文件进行写入!\n");
        return;
    }

    for (int i = 0; i < count; i++) {
        fprintf(file, "%d|%s|%s|%s\n", 
                singers[i].id, 
                singers[i].first_name, 
                singers[i].last_name, 
                singers[i].genre);
    }
    fclose(file);
    printf("文本格式数据已保存!\n");
}

3. 新增、编辑、删除功能

  • 新增:检查容量→扩容(如果需要)→接收用户输入→赋值给数组末尾,singer_count++
  • 编辑:根据ID遍历数组找到目标歌手→更新对应字段
  • 删除:找到目标歌手索引→将后面的元素依次往前移动一位→singer_count--

举个新增的例子:

void add_singer(Singer **singers, int *capacity, int *count) {
    // 先检查扩容
    if (*count == *capacity) {
        *capacity *= 2;
        Singer *temp = realloc(*singers, *capacity * sizeof(Singer));
        if (!temp) {
            printf("内存不足,添加失败!\n");
            return;
        }
        *singers = temp;
    }

    // 接收用户输入
    printf("请输入歌手ID:");
    scanf("%d", &(*singers)[*count].id);
    // 注意:用getchar()清除scanf留下的换行符,避免fgets直接读取空行
    getchar();
    
    printf("请输入姓名:");
    fgets((*singers)[*count].first_name, 50, stdin);
    // 去掉fgets读取的换行符
    (*singers)[*count].first_name[strcspn((*singers)[*count].first_name, "\n")] = '\0';

    printf("请输入姓氏:");
    fgets((*singers)[*count].last_name, 50, stdin);
    (*singers)[*count].last_name[strcspn((*singers)[*count].last_name, "\n")] = '\0';

    printf("请输入流派:");
    fgets((*singers)[*count].genre, 30, stdin);
    (*singers)[*count].genre[strcspn((*singers)[*count].genre, "\n")] = '\0';

    (*count)++;
    printf("歌手添加成功!\n");
}
三、菜单逻辑整合

把这些功能放到你的菜单switch-case里就行,比如:

int main() {
    Singer *singers = malloc(21 * sizeof(Singer));
    int current_capacity = 21;
    int singer_count = 0;
    int choice;

    do {
        printf("\n===== 歌手管理系统 =====\n");
        printf("1. 保存数据到文件\n");
        printf("2. 展示所有歌手\n");
        printf("3. 添加新歌手\n");
        printf("4. 编辑歌手信息\n");
        printf("5. 删除歌手\n");
        printf("6. 退出\n");
        printf("请输入选择:");
        scanf("%d", &choice);

        switch(choice) {
            case 1:
                save_to_file(singers, singer_count);
                break;
            case 2:
                display_singers(singers, singer_count);
                break;
            case 3:
                add_singer(&singers, &current_capacity, &singer_count);
                break;
            case 4:
                // 实现edit_singer函数
                edit_singer(singers, singer_count);
                break;
            case 5:
                // 实现delete_singer函数
                delete_singer(singers, &singer_count);
                break;
            case 6:
                printf("退出程序...\n");
                break;
            default:
                printf("无效选择,请重新输入!\n");
        }
    } while(choice != 6);

    free(singers);
    return 0;
}

额外注意点

  • 要保证ID的唯一性,新增/编辑时可以加个检查逻辑,避免重复ID
  • 处理用户输入时,尽量用fgets代替scanf读取字符串,避免缓冲区溢出和换行符问题
  • 每次动态分配内存后都要检查是否成功,避免空指针错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:30:34