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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:00:53