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

使用realloc出现段错误:C语言播放列表程序的问题排查与修复

C语言播放列表程序段错误问题分析与修复

问题背景

这是一道已结束的公开大学编程考试题,要求开发C语言程序管理歌曲播放列表:每首歌包含标题和时长(秒),用两个独立动态数组存储标题和时长,通过指针算术实现添加、删除、更新、显示功能。

用户尝试的代码如下:

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

const size_t MAX_NAME_LENGTH = 20;
size_t number_of_songs = 0;

char* names_of_songs;
int* durations;

void add(char* name_of_song, int duration) {
    number_of_songs++;

    names_of_songs = realloc(name_of_song, MAX_NAME_LENGTH * number_of_songs);
    durations = realloc(durations, number_of_songs * sizeof(int));

    int start_location = ((number_of_songs - 1) * MAX_NAME_LENGTH) + 1;
    char* name_location = &name_of_song[start_location];

    strncpy(name_location, name_of_song, MAX_NAME_LENGTH);

    names_of_songs[start_location + strlen(name_of_song) + 1] = 0;
    durations[number_of_songs - 1] = duration;
}

void display() {
    for (size_t i = 0; i < number_of_songs; i++) {
        int position_in_name = 0;
        int start_location = (i * MAX_NAME_LENGTH) + 1;
        char character = 1;

        printf("Name: ");
        while (character != 0) {
            character = names_of_songs[start_location++];
            printf("%c", character);
        }

        printf(", Duration: %d sec\n", durations[i]);
    }
}

int main(int argc, char const *argv[]) {
    names_of_songs = malloc(1);
    durations = malloc(1);

    display();
    add("Something", 2);
    display();
    add("Something else", 32);
    display();
    add("boring song", 4);
    display();

    free(names_of_songs);
    free(durations);

    return 0;
}

运行时出现段错误,调试发现首次调用realloc时出错,需解决:问题出在哪?如何修复?这类问题怎么调试?


错误原因分析

  1. realloc参数非法:首次调用add时,names_of_songs = realloc(name_of_song, ...)是致命错误——realloc的第一个参数必须是malloc/calloc/realloc分配的堆内存指针,而name_of_song是传入的字符串字面量指针(比如"Something"),属于只读内存区域,直接用它调用realloc会触发未定义行为,导致段错误。
  2. 字符串存储逻辑混乱:
    • 计算start_location时多余的+1,每首歌的起始位置应为(number_of_songs-1)*MAX_NAME_LENGTH,额外加1会浪费字节且导致越界。
    • 赋值name_location时错误操作了传入的字符串指针,而非全局动态数组names_of_songs,等于没往目标内存写数据。
    • 手动添加字符串结束符的位置错误,strncpy不会自动补\0,且计算的索引明显超出分配范围。
  3. 初始内存分配不合理:main里malloc(1)给两个数组毫无意义,初始化为NULL即可,因为realloc(NULL, size)等价于malloc(size)。

修复后的代码

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

const size_t MAX_NAME_LENGTH = 20;
size_t number_of_songs = 0;

char* names_of_songs = NULL;
int* durations = NULL;

void add(const char* name_of_song, int duration) {
    number_of_songs++;

    // 先临时存储realloc结果,避免分配失败丢失原指针
    char* temp_names = realloc(names_of_songs, MAX_NAME_LENGTH * number_of_songs);
    int* temp_durations = realloc(durations, number_of_songs * sizeof(int));
    
    // 内存分配失败处理(考试可简化,实际开发必须检查)
    if (!temp_names || !temp_durations) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    names_of_songs = temp_names;
    durations = temp_durations;

    // 计算当前新歌在数组中的起始位置
    size_t start_idx = (number_of_songs - 1) * MAX_NAME_LENGTH;
    char* target = names_of_songs + start_idx;

    // 复制标题并强制添加结束符,避免越界
    strncpy(target, name_of_song, MAX_NAME_LENGTH - 1);
    target[MAX_NAME_LENGTH - 1] = '\0';

    durations[number_of_songs - 1] = duration;
}

void display() {
    if (number_of_songs == 0) {
        printf("播放列表为空\n");
        return;
    }
    for (size_t i = 0; i < number_of_songs; i++) {
        size_t start_idx = i * MAX_NAME_LENGTH;
        printf("歌曲%d: 标题: %s, 时长: %d秒\n", 
               (int)(i+1), names_of_songs + start_idx, durations[i]);
    }
    printf("\n");
}

int main(int argc, char const *argv[]) {
    display();
    add("Something", 2);
    display();
    add("Something else", 32);
    display();
    add("boring song", 4);
    display();

    free(names_of_songs);
    free(durations);

    return 0;
}

调试方法建议

  1. GDB调试:
    • 编译时加-g生成调试信息:gcc -g playlist.c -o playlist
    • 启动GDB后用run执行程序,触发段错误时用backtrace查看调用栈,定位出错代码行;用print查看指针、变量值,对比是否合法。
  2. 编译警告检查:
    • 打开最高级别警告编译:gcc -Wall -Wextra -Werror playlist.c -o playlist,编译器会提前揪出很多逻辑错误(比如把字符串字面量传给非const指针)。
  3. 内存检查工具:
    • 用valgrind运行程序:valgrind ./playlist,它会检测内存越界、非法指针操作、内存泄漏等问题,给出详细报告。
  4. 分步打印调试:
    • 在realloc前后、指针赋值处打印地址和变量值,比如printf("names_of_songs地址: %p\n", names_of_songs);,观察指针是否异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 14:07:51