使用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时出错,需解决:问题出在哪?如何修复?这类问题怎么调试?
错误原因分析
realloc参数非法:首次调用add时,names_of_songs = realloc(name_of_song, ...)是致命错误——realloc的第一个参数必须是malloc/calloc/realloc分配的堆内存指针,而name_of_song是传入的字符串字面量指针(比如"Something"),属于只读内存区域,直接用它调用realloc会触发未定义行为,导致段错误。- 字符串存储逻辑混乱:
- 计算
start_location时多余的+1,每首歌的起始位置应为(number_of_songs-1)*MAX_NAME_LENGTH,额外加1会浪费字节且导致越界。 - 赋值
name_location时错误操作了传入的字符串指针,而非全局动态数组names_of_songs,等于没往目标内存写数据。 - 手动添加字符串结束符的位置错误,
strncpy不会自动补\0,且计算的索引明显超出分配范围。
- 计算
- 初始内存分配不合理:
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; }
调试方法建议
- GDB调试:
- 编译时加
-g生成调试信息:gcc -g playlist.c -o playlist - 启动GDB后用
run执行程序,触发段错误时用backtrace查看调用栈,定位出错代码行;用print查看指针、变量值,对比是否合法。
- 编译时加
- 编译警告检查:
- 打开最高级别警告编译:
gcc -Wall -Wextra -Werror playlist.c -o playlist,编译器会提前揪出很多逻辑错误(比如把字符串字面量传给非const指针)。
- 打开最高级别警告编译:
- 内存检查工具:
- 用
valgrind运行程序:valgrind ./playlist,它会检测内存越界、非法指针操作、内存泄漏等问题,给出详细报告。
- 用
- 分步打印调试:
- 在
realloc前后、指针赋值处打印地址和变量值,比如printf("names_of_songs地址: %p\n", names_of_songs);,观察指针是否异常。
- 在
内容的提问来源于stack exchange,提问作者eccentricOrange
相关产品推荐
相关产品推荐

