C语言二级指针作为函数参数的链表插入调用问题求助
嘿,我马上就发现了你代码里的核心问题,结合你说的长期用Java/C#的背景,其实就是C语言里指针传递和值传递的差异搞混了,还有链表头部插入的逻辑小疏漏,咱们一步步来改:
1. Push函数调用的传参错误
你的push函数定义是void push(rilevamenti_t ** head, ...),需要的是链表头指针的地址(也就是指针的指针),但你在main里调用的时候传的是lista1——这只是一个rilevamenti_t *类型的指针变量,相当于把指针的值传进去了,函数内部修改*head根本影响不到main里的lista1,结果就是链表永远建不起来,lista1一直是NULL。
修正方式:调用push时传入lista1的地址,也就是:
push(&lista1, tmpIdStazione, tmpNomeParametro, tmpCodiceParametro, tmpQuantita, tmpData);
2. 链表头部插入的逻辑缺失
你现在的push函数里只做了*head = new_rilevamento;,这会直接把新节点设为头,但原来的头节点会被彻底丢掉(还会造成内存泄漏)。正确的头部插入应该先让新节点的next指向原来的头,再把新节点更新为新的头:
修正后的push函数核心逻辑:
void push(rilevamenti_t ** head, char id[], char nome[], char codice[], float quant, char data[]) { rilevamenti_t * new_rilevamento = malloc(sizeof(rilevamenti_t)); // 一定要检查malloc是否成功!C里不会自动处理内存分配失败 if (new_rilevamento == NULL) { perror("Failed to allocate memory for new node"); return; } // 填充结构体成员(补充完整你的省略逻辑) strncpy(new_rilevamento->idStazione, id, sizeof(new_rilevamento->idStazione)-1); // 手动添加字符串结束符,strncpy不会自动补全 new_rilevamento->idStazione[sizeof(new_rilevamento->idStazione)-1] = '\0'; strncpy(new_rilevamento->nomeParametro, nome, sizeof(new_rilevamento->nomeParametro)-1); new_rilevamento->nomeParametro[sizeof(new_rilevamento->nomeParametro)-1] = '\0'; strncpy(new_rilevamento->codiceParametro, codice, sizeof(new_rilevamento->codiceParametro)-1); new_rilevamento->codiceParametro[sizeof(new_rilevamento->codiceParametro)-1] = '\0'; new_rilevamento->quantita = quant; strncpy(new_rilevamento->data, data, sizeof(new_rilevamento->data)-1); new_rilevamento->data[sizeof(new_rilevamento->data)-1] = '\0'; // 关键:先让新节点指向原头节点 new_rilevamento->next = *head; // 再把新节点设为新的头 *head = new_rilevamento; }
3. 文件读取的逻辑优化
你原来用while(1)加feof的写法有坑——feof只有在读取到文件末尾之后才会返回真,也就是说当最后一次fscanf读取失败时,你还是会执行push,把临时变量里的无效数据插入链表。
修正后的读取循环:
// 直接用fscanf的返回值判断是否成功读取所有字段 while (fscanf(fp, "%s %s %s %f %s", tmpIdStazione, tmpNomeParametro, tmpCodiceParametro, &tmpQuantita, tmpData) == 5) { printf("Stazione: %s\n", tmpIdStazione ); push(&lista1, tmpIdStazione, tmpNomeParametro, tmpCodiceParametro, tmpQuantita, tmpData); }
这样只有当成功读取5个字段时才处理,彻底避免无效数据插入。
补充说明(帮你回忆C和Java/C#的差异)
Java/C#里的对象是引用传递,但C语言里只有值传递——如果你想在函数里修改一个变量(比如这里的链表头指针),必须把这个变量的地址传进去。rilevamenti_t **就是“指针的指针”,它让函数能直接操作main里的lista1变量,而不是操作它的副本。
内容的提问来源于stack exchange,提问作者Geologo

