如何从命令行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
相关产品推荐
相关产品推荐

