C语言中用指针替代数组实现链表节点添加的异常问题排查
问题与修复方案
问题现象
实现链表数字列表程序时,计划用atoi转换用户输入的整数并反向添加到链表(如原链表3,4,5添加1,2后预期为1,2,3,4,5),但添加节点时仅第一个值正确,其余均为垃圾值,且原链表数据会丢失。
核心错误点
- 数组指针赋值错误:
main中*numbers = *nodes(input, amount);仅复制了nodes返回数组的第一个元素,未完整复制整个数组,且重复malloc导致内存泄漏。 - 循环终止条件错误:
add函数中用numbers[a] != '\0'判断数组结束,int数组不会以'\0'(即0)作为结束标志,若输入包含0会提前终止,否则会越界访问垃圾值。 - 原链表提前释放:
main中打印原链表后直接释放了所有节点,导致后续添加时原链表已为空,无法实现追加效果。 - 数组内存分配不通用:
nodes函数中malloc(amount * 4)硬编码int字节数,应使用sizeof(int)保证跨平台兼容性。 - 输入循环条件错误:
main中的输入验证循环用c != 'd' || c != 'a' || c != 'f'作为条件,逻辑永远为真,会导致无限循环,应改为逻辑与c != 'd' && c != 'a' && c != 'f'。
修复后的完整代码
// Implements a list of numbers using a linked list #include <cs50.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct node { int number; struct node *next; } node; node *makelist(int argc, char *argv[]); int check_spaces(char *input); int *nodes(char *input, int amount); node *add(node *list, int *numbers, int amount); /*int delete(); int find();*/ int main(int argc, char *argv[]) { // Get linked list node *list = makelist(argc, argv); // Print linked list for (node *ptr = list; ptr != NULL; ptr = ptr->next) { printf("%i ", ptr->number); } printf("\n"); char c; // Give choices printf("Do you want to (d)delete a node, (a)add a node, or (f)find a node? "); // Repeat if incorrect input do { // 处理输入缓冲区的换行符 scanf(" %c", &c); if (c == 'd' || c == 'a' || c == 'f') { break; } else { printf("Wrong input, try again. "); } } while (c != 'd' && c != 'a' && c != 'f'); // 修正循环条件 // If user chose to "add" node(s) if (c == 'a') { char *input = get_string("What are the node(s) that you want to add? "); int amount = check_spaces(input); int *numbers = nodes(input, amount); // 直接接收nodes返回的指针,无需重复malloc list = add(list, numbers, amount); // 打印更新后的链表 printf("Updated list: "); for (node *ptr2 = list; ptr2 != NULL; ptr2 = ptr2->next) { printf("%i ", ptr2->number); } printf("\n"); free(numbers); } // If user chose to "delete" node(s) else if (c == 'd') { printf("Delete function not implemented yet.\n"); } // If user chose to "find" node(s) else { printf("Find function not implemented yet.\n"); } // 最后统一释放链表内存 node *ptr = list; while (ptr != NULL) { node *next = ptr->next; free(ptr); ptr = next; } return 0; } // Make linked list node *makelist(int argc, char *argv[]) { // Memory for numbers node *list = NULL; // For each command-line argument for (int i = 1; i < argc; i++) { // Convert argument to int int number = atoi(argv[i]); // Allocate node for number node *n = malloc(sizeof(node)); if (n == NULL) { // 内存分配失败时,释放已分配的节点 node *ptr = list; while (ptr != NULL) { node *next = ptr->next; free(ptr); ptr = next; } return NULL; } n->number = number; n->next = NULL; // If list is empty if (list == NULL) { // This node is the whole list list = n; } // If list has numbers already else { // Iterate over nodes in list for (node *ptr = list; ptr != NULL; ptr = ptr->next) { // If at end of list if (ptr->next == NULL) { // Append node ptr->next = n; break; } } } } return list; } // Add function: 将numbers数组中的元素按顺序头插,实现1,2添加后在原链表前 node *add(node *list, int *numbers, int amount) { // 遍历numbers数组,依次头插每个元素 for (int i = 0; i < amount; i++) { node *n = malloc(sizeof(node)); if (n == NULL) { // 内存分配失败时,释放已分配的新节点和原链表 node *ptr = list; while (ptr != NULL) { node *next = ptr->next; free(ptr); ptr = next; } return NULL; } n->number = numbers[i]; // Prepend node to list n->next = list; list = n; } return list; } // Parse input string to int array int *nodes(char *input, int amount) { int *y = malloc(amount * sizeof(int)); if (y == NULL) { return NULL; } char *x = input; y[0] = atoi(x); int i = 0; int j = 1; while (input[i] != '\0') { if (input[i] == ' ') { // 跳过连续空格 while (input[i+1] == ' ') { i++; } x = &input[i+1]; y[j] = atoi(x); j++; } i++; } return y; } // Count number of numbers in input string int check_spaces(char *input) { if (strlen(input) == 0) { return 0; } int numbers = 1; int i = 0; while (input[i] != '\0') { if (input[i] == ' ') { // 跳过连续空格,避免重复计数 while (input[i+1] == ' ') { i++; } numbers++; } i++; } return numbers; } /*int delete() { } int find() { }*/
关键修复说明
- 修正数组指针赋值:删除
main中重复的malloc操作,直接用int *numbers = nodes(input, amount);接收动态数组指针,避免内存泄漏和不完整复制。 - 修正循环逻辑:
add函数改用已知的amount作为循环次数,确保遍历所有输入数字;修正输入验证循环的逻辑运算符,避免无限循环。 - 调整内存释放时机:将原链表的释放操作移到程序末尾,保证添加节点时原链表数据存在。
- 优化输入处理:在
check_spaces和nodes函数中处理连续空格,避免重复计数或转换错误;修正scanf格式符,跳过输入缓冲区的换行符。 - 添加容错处理:在
makelist和add函数中添加内存分配失败时的资源清理逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Harr
相关产品推荐
相关产品推荐

