C语言调用qsort或重复添加怪物时出现段错误求助
C语言程序段错误排查:qsort调用与重复添加怪物问题
问题描述
运行C语言程序时反复出现段错误,触发场景主要有两个:调用qsort函数时,或是添加多个怪物后尝试加入已存在的怪物时。
代码与数据
头文件(utils.h)
#ifndef UTILS_H_ #define UTILS_H_ #define MAX_MONSTERS 32 #define MAX_LEN 128 #define FILTER_NAME 1 #define FILTER_HP 2 #define FILTER_AC 3 #define FILTER_STATS 4 #define EXIT_CHOICE 5 void trim(char *); typedef struct { char name[MAX_LEN]; char type[MAX_LEN]; int hp; int ac; int str; int dex; int con; } monster_s; struct node_s { void *data; // 指向monster_s结构体的指针 int roll; // 掷骰结果 int position; // 排序后的原始数组位置 struct node_s *next; }; int load_monster_csv(monster_s **, FILE *); void add_to_order(monster_s *, struct node_s **, int, int*); void display_order(struct node_s *); void parse_monster(monster_s *, char *); #endif
工具实现文件(utils.c)
#include <string.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <ctype.h> #include "utils.h" void trim(char *str) { int l = strlen(str); if (str[l - 1] == '\n') { str[l - 1] = 0; } } int is_in_order(monster_s target, struct node_s *initiativeOrder, int count) { for (int i = 0; i < count; i++) { monster_s *current_monster = (monster_s *)initiativeOrder[i].data; if (current_monster == NULL) { printf("Assignment failed I don't know why."); continue; } printf("Checking %s against %s\n", target.name, current_monster->name); if (!strcmp(target.name, current_monster->name)) { // 怪物已在队列中 return 1; } } return 0; } struct node_s *enqueue(struct node_s *head, monster_s *data, int pos, int roll) { struct node_s *new_node = calloc(1, sizeof(struct node_s)); new_node->data = data; new_node->next = NULL; new_node->position = pos; new_node->roll = roll; if (head == NULL) { head = new_node; } else { struct node_s *current = head; while (current->next != NULL) { current = current->next; } current->next = new_node; } return head; } void add_to_order(monster_s *monsters_ptr, struct node_s **initiativeOrder, int numbersRead, int* added_count) { int count = 0; char input[MAX_LEN]; do { for (int i = 0; i < numbersRead; i++) { printf("(%d) %s\n", i + 1, monsters_ptr[i].name); } printf("(C) Continue\n"); scanf(" %s", input); if (isdigit(input[0])) { int idx = atoi(input) - 1; if(idx >= 0 && idx < numbersRead) { int added = is_in_order(monsters_ptr[idx], *initiativeOrder, count); if (added) { printf("%s has already been added.\n", monsters_ptr[idx].name); } else { *initiativeOrder = realloc(*initiativeOrder, sizeof(struct node_s) * (count + 1)); (*initiativeOrder)[count].data = &monsters_ptr[idx]; (*initiativeOrder)[count].next = NULL; printf("%s added to order.\n", monsters_ptr[idx].name); count++; } } else { printf("Invalid Input\n"); } } else { switch (input[0]) { case 'C': break; default: printf("Invalid Input\n"); break; } } if (input[0] == 'C') { break; } } while (1); *added_count = count; } void parse_monster(monster_s *monster, char *csv_line) { char *token = strtok(csv_line, ","); int attr = 0; while (token) { switch (attr) { case 0: strcpy(monster->name, token); break; case 1: strcpy(monster->type, token); break; case 2: monster->hp = atoi(token); break; case 3: monster->ac = atoi(token); break; case 4: monster->str = atoi(token); break; case 5: monster->dex = atoi(token); break; case 6: monster->con = atoi(token); break; } attr++; token = strtok(NULL, ","); } } int load_monster_csv(monster_s **monsters_ptr, FILE *fp) { char buffer[MAX_LEN] = {0}; int num_read = 0; while (fgets(buffer, MAX_LEN, fp)) { trim(buffer); monster_s *temp_monster = (monster_s *)malloc(sizeof(monster_s)); parse_monster(temp_monster, buffer); *monsters_ptr = (monster_s *)realloc(*monsters_ptr, sizeof(monster_s) * (num_read + 1)); // 注意(*monster_ptr)的括号,否则会触发段错误 (*monsters_ptr)[num_read] = *temp_monster; free(temp_monster); num_read++; } return num_read; }
主程序(main.c)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "utils.h" int roll_dice() { int dice = (rand() % 20) + 1; printf("%d", dice); return dice; } void roll_order(struct node_s *initiativeOrder, int count) { for(int i=0; i<count; i++) { initiativeOrder[i].roll = roll_dice(); } } int compare_rolls(const void *elem1, const void *elem2) { struct node_s *turnOrderA = (struct node_s *)elem1; struct node_s *turnOrderB = (struct node_s *)elem2; return turnOrderB->roll - turnOrderA->roll; } int main(int argc, char *argv[]) { // 初始化随机数种子,只调用一次 srand(time(0)); // 校验输入参数 if (argc == 1) { printf("Incorrect Input\nCorrect Input: ./a.out FILEPATH"); return 1; } FILE *fp = NULL; // 打开命令行参数指定的文件 fp = fopen(argv[1], "r"); if (fp == NULL) { printf("Could not open file.\n"); return 1; } monster_s *monsters_ptr = NULL; struct node_s *initiativeOrder = NULL; int numbersRead = load_monster_csv(&monsters_ptr, fp); fclose(fp); int added_count = 0; add_to_order(monsters_ptr, &initiativeOrder, numbersRead, &added_count); if(added_count > 0) { roll_order(initiativeOrder, added_count); qsort(initiativeOrder, added_count, sizeof(struct node_s), compare_rolls); } // 释放动态分配的内存 free(initiativeOrder); free(monsters_ptr); return 0; }
CSV数据
Acolyte,HUMANOID,9,10,5,5,5 Adult Black Dragon,DRAGON,195,19,11,7,10 Goblin,HUMANOID,7,15,4,7,5 Gold Dragon Wyrmling,DRAGON,60,17,9,7,8 Bearded Devil,FIEND,52,13,8,7,7 Rakshasa,FIEND,110,16,7,8,9
已尝试的操作
曾尝试将struct node_s *initiativeOrder = NULL;改为struct node_s initiativeOrder;并传递地址,但问题仍未解决。
核心错误与修复方案
1. 指针传递错误导致内存访问非法
add_to_order函数原本接收的是struct node_s *initiativeOrder值传递,函数内realloc修改的是局部指针,主函数中的原指针不会更新,后续qsort会访问未初始化的栈内存。
- 修复:改为传递指针的指针
struct node_s **initiativeOrder,确保主函数能同步获取realloc后的新地址。
2. is_in_order函数数组越界
代码中current_monster[i].name是错误用法:current_monster已经是单个怪物的指针,不需要再用下标i,直接用current_monster->name即可,否则会导致指针越界访问随机内存。
3. qsort调用的元素数量错误
主函数中原本用numbersRead(CSV总怪物数)作为qsort的元素数量,但实际排序的是用户选择添加的怪物,应该传递实际添加的added_count。
4. 未初始化的结构体与非法realloc
原本主函数中struct node_s initiativeOrder;是栈上未初始化的结构体,realloc无法对栈地址进行重新分配,必须初始化为NULL作为动态数组的起点。
5. 随机数生成重复问题
roll_dice中每次调用都执行srand(time(0)),短时间内多次调用会导致time(0)返回相同值,生成重复随机数。修复:将srand(time(0))放在主函数开头只执行一次。
内容的提问来源于stack exchange,提问作者xxi0
相关产品推荐
相关产品推荐

