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

如何从命令行argv动态分配字符串 解决链表增删内存错误

修复方案

核心错误点

  • 头指针head未初始化,属于野指针,后续链表操作会直接触发段错误
  • 内存分配逻辑错误:malloc申请堆内存后直接把name指针赋值为argv[x],丢失了malloc返回的堆内存地址,既造成内存泄漏,后续free(name)实际尝试释放命令行参数所在的只读内存段,是free(): invalid pointer错误的直接原因
  • 参数遍历逻辑错误:循环从x=0开始会把程序名argv[0]当作业务参数处理,应该从x=1开始遍历
  • 指针操作错误:printf和insert/removeNode中两次调用name++,会导致实际传入的名称跳过第一个字符,同时修改了name的初始地址,导致free时传入的不是malloc返回的首地址
  • 字符串比较错误:removeNode中用==比较字符串,实际是对比指针地址而非字符串内容,永远匹配不到链表中存储的字符串,需改用strcmp
  • 空指针判断缺失:removeNode的while循环中,current = current->next后未判断current是否为NULL就直接访问current->data,遍历到链表尾部时会触发段错误
  • 链表存储逻辑缺失:未把名称字符串复制到链表节点的独立内存中,直接存储临时指针后续释放后会产生野指针

修正后代码

main函数

int main(int argc, char *argv[]){    
    struct node* head = NULL; // 初始化头指针
    // 跳过argv[0]程序名,从第一个业务参数开始遍历
    for(int x = 1; x < argc; x++){
        int arg_len = strlen(argv[x]);
        // 分配内存存储去掉+/-前缀的名称,长度为arg_len(去掉1位前缀)+1存结束符
        char *name = malloc(arg_len);
        strcpy(name, argv[x] + 1);

        if(argv[x][0] == '+'){
            printf("adding %s \n", name);
            // insert函数需将name存储到节点data字段,后续链表销毁时统一释放
            insert(&head, name);
            printf("List: ");
            printList(&head);
        }
        else if(argv[x][0] == '-'){
            printf("removing %s \n", name);
            char *removed_name = removeNode(&head, name);
            // 释放removeNode返回的堆内存
            if(strcmp(removed_name, "error0") != 0) {
                free(removed_name);
            }
            // 释放临时匹配用的name内存
            free(name);
        }
    }
    // 程序退出前遍历释放整个链表的节点和对应字符串内存,避免泄漏
    return 0;
}

removeNode函数

char * removeNode(struct node** head, char* name){
    struct node *current = *head;
    struct node *previous = NULL;

    if(current == NULL){
        return "error0";
    }

    // 用strcmp比较字符串内容而非地址
    if(strcmp(current->data, name) == 0){
        struct node * node2delete = *head;
        *head = node2delete->next;
        char * name2 = malloc(strlen(name) + 1);
        strcpy(name2, name);
        // 先释放节点存储的字符串,再释放节点本身
        free(node2delete->data);
        free(node2delete);
        printf("Removed %s \n", name);
        return name2;
    }
    // 提前判断next不为空,避免访问空指针
    while(current->next != NULL){
        previous = current;
        current = current->next;
        if(strcmp(current->data, name) == 0){
            previous->next = current->next;
            char * name2 = malloc(strlen(name) + 1);
            strcpy(name2, name);
            free(current->data);
            free(current);
            printf("Removed %s \n", name);
            return name2;
        }
    }
    return "error0";
}

补充说明

  • 如果你的insert函数内部会复制传入的字符串,那插入操作完成后可以直接释放当前的name内存,避免后续还要在链表销毁时处理。
  • 生产环境下所有malloc调用都需要做返回值判空,避免内存申请失败触发段错误,示例为简化逻辑省略了该部分。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 23:45:05