C语言链表指针问题求助:typedef疑问、编译错误及节点插入
问题描述
我正在做《How to program in C》一书中的链表习题,题目要求所有链表操作需在main函数内完成。题目给定初始链表由startPtr指向,包含两个按字母序排列的节点:存储"Jones"和"Smith",需要按顺序插入以下三个节点的数据:
"Adams" 85.0 "Thompson" 73.5 "Pritchard" 66.5
要求使用previousPtr、currentPtr和newPtr完成插入,需说明每次插入前previousPtr和currentPtr的指向,且假设newPtr已指向分配好数据的新节点。
我编写的代码如下:
//fig 12_4.c #include <stdlib.h> #include <stdio.h> #include <string.h> struct gradeNode{ char lastName[20]; double grade; struct gradeNode *nextPtr; }; typedef struct gradeNode GradeNode; typedef GradeNode *GradeNodePtr; void insert(GradeNodePtr); int main(void) { //part a GradeNodePtr startPtr = NULL; //part b GradeNodePtr newPtr = malloc(sizeof(GradeNode)); startPtr = newPtr; // checking to see if memory was allocated properly if(newPtr != NULL) { newPtr->grade = 91.5; strcpy(newPtr->lastName,"Jones"); newPtr->nextPtr = NULL; } //part c //Creating the previousPtr, currentPtr, and NewPtr to make insertions GradeNodePtr previousPtr = NULL; // intializing both the previousPtr and currentPtr to begin insertion process GradeNodePtr currentPtr = *startPtr; for(int i; i>4; i++) { if(currentPtr != NULL) { puts("Please enter a last name and grade"); scanf("%s\n%d",currentPtr->lastName,currentPtr->grade); previousPtr = currentPtr; currentPtr = currentPtr->nextPtr; } else { puts("Critical Error encountered closing program!"); break; } } }
我有两个疑问:
- 代码中使用
typedef定义了GradeNode和GradeNodePtr(即GradeNode *),为何要对GradeNode使用间接运算符?这会对该类型声明的变量产生什么影响? - 语句
GradeNodePtr currentPtr = *startPtr;编译报错,提示类型不兼容,我的理解(currentPtr需指向startPtr内的数据而非地址)是否错误?
此外,希望得到针对该题目代码的改进建议。
疑问解答与代码改进建议
疑问1解答
首先明确:typedef GradeNode *GradeNodePtr;里的间接运算符是用来定义指针类型别名的,不是对GradeNode用间接运算符。
typedef struct gradeNode GradeNode;是把struct gradeNode这个结构体类型简化成GradeNode,避免后续重复写冗长的struct gradeNode,让代码更简洁。typedef GradeNode *GradeNodePtr;是把GradeNode *(指向GradeNode结构体的指针类型)简化成GradeNodePtr。后续声明GradeNodePtr ptr;就等价于GradeNode *ptr;,本质就是声明一个指向结构体的指针变量,没有特殊影响,只是让链表操作的代码更易读、少冗余。
疑问2解答
你的理解完全错误,编译报错的核心原因是类型不匹配:
GradeNodePtr是指针类型,所以currentPtr需要存储地址值。startPtr本身就是GradeNodePtr类型(指针),*startPtr是对指针解引用,得到的是GradeNode类型的结构体实例,不是地址。把结构体实例赋值给指针变量,类型自然不兼容。- 正确写法是
GradeNodePtr currentPtr = startPtr;,这样currentPtr就和startPtr指向同一个节点的地址,符合链表遍历的初始需求。
代码改进建议
1. 先构建题目要求的初始链表
你当前只创建了"Jones"节点,需要补充创建"Smith"节点并完成链接:
// 创建Jones节点后,继续创建Smith节点 GradeNodePtr smithPtr = malloc(sizeof(GradeNode)); if (smithPtr != NULL) { strcpy(smithPtr->lastName, "Smith"); smithPtr->grade = 88.0; // 题目未指定分数,可自行设定合理值 smithPtr->nextPtr = NULL; jonesPtr->nextPtr = smithPtr; // 将Jones节点的next指向Smith节点 }
2. 修正插入逻辑(按字母序插入指定节点)
题目要求插入给定的三个节点,不是用户输入,需替换scanf逻辑,实现按字母序插入的核心流程:
对于每个要插入的节点:
- 初始化
previousPtr = NULL,currentPtr = startPtr - 循环遍历链表:当
currentPtr不为空,且strcmp(newNode->lastName, currentPtr->lastName) > 0时,说明当前节点姓氏字典序更小,继续向后找位置,此时previousPtr = currentPtr,currentPtr = currentPtr->nextPtr - 插入分三种情况:
- 若
previousPtr == NULL:插入到链表头部,newNode->nextPtr = startPtr,startPtr = newNode - 若
currentPtr == NULL:插入到链表尾部,previousPtr->nextPtr = newNode,newNode->nextPtr = NULL - 中间位置:
newNode->nextPtr = currentPtr,previousPtr->nextPtr = newNode
- 若
3. 修正无效循环
你写的for(int i; i>4; i++)是无效循环(i未初始化,且初始条件i>4不成立),需替换为遍历指定插入数据的逻辑,比如用数组存储待插入数据后循环处理。
4. 完善内存检查与资源释放
每次malloc后必须检查是否分配成功;程序结束前需释放链表所有节点内存,避免内存泄漏。
5. 新增链表打印函数(验证结果)
可以添加一个打印函数,方便验证插入后的链表是否符合预期:
void printList(GradeNodePtr startPtr) { GradeNodePtr current = startPtr; while (current != NULL) { printf("%s %.1f\n", current->lastName, current->grade); current = current->nextPtr; } }
完整改进后的核心代码示例
#include <stdlib.h> #include <stdio.h> #include <string.h> struct gradeNode{ char lastName[20]; double grade; struct gradeNode *nextPtr; }; typedef struct gradeNode GradeNode; typedef GradeNode *GradeNodePtr; void printList(GradeNodePtr startPtr) { GradeNodePtr current = startPtr; while (current != NULL) { printf("%s %.1f\n", current->lastName, current->grade); current = current->nextPtr; } } int main(void) { // 构建初始链表:Jones -> Smith GradeNodePtr startPtr = NULL; // 创建Jones节点 GradeNodePtr jonesPtr = malloc(sizeof(GradeNode)); if (jonesPtr != NULL) { strcpy(jonesPtr->lastName, "Jones"); jonesPtr->grade = 91.5; jonesPtr->nextPtr = NULL; startPtr = jonesPtr; } // 创建Smith节点并链接 GradeNodePtr smithPtr = malloc(sizeof(GradeNode)); if (smithPtr != NULL) { strcpy(smithPtr->lastName, "Smith"); smithPtr->grade = 88.0; smithPtr->nextPtr = NULL; jonesPtr->nextPtr = smithPtr; } // 定义要插入的三个数据 typedef struct { char lastName[20]; double grade; } InsertData; InsertData insertData[] = { {"Adams", 85.0}, {"Thompson", 73.5}, {"Pritchard", 66.5} }; int dataCount = sizeof(insertData) / sizeof(insertData[0]); for (int i = 0; i < dataCount; i++) { // 分配新节点并赋值 GradeNodePtr newPtr = malloc(sizeof(GradeNode)); if (newPtr == NULL) { puts("Memory allocation failed."); break; } strcpy(newPtr->lastName, insertData[i].lastName); newPtr->grade = insertData[i].grade; newPtr->nextPtr = NULL; GradeNodePtr previousPtr = NULL; GradeNodePtr currentPtr = startPtr; // 打印插入前指针指向 printf("\n插入%s前:previousPtr指向%s,currentPtr指向%s\n", newPtr->lastName, previousPtr ? previousPtr->lastName : "NULL", currentPtr ? currentPtr->lastName : "NULL"); // 查找插入位置 while (currentPtr != NULL && strcmp(newPtr->lastName, currentPtr->lastName) > 0) { previousPtr = currentPtr; currentPtr = currentPtr->nextPtr; // 打印移动后的指针指向 printf("移动指针后:previousPtr指向%s,currentPtr指向%s\n", previousPtr->lastName, currentPtr ? currentPtr->lastName : "NULL"); } // 执行插入 if (previousPtr == NULL) { newPtr->nextPtr = startPtr; startPtr = newPtr; } else { previousPtr->nextPtr = newPtr; newPtr->nextPtr = currentPtr; } } // 打印最终链表 puts("\n最终链表内容:"); printList(startPtr); // 释放内存 GradeNodePtr tempPtr; while (startPtr != NULL) { tempPtr = startPtr; startPtr = startPtr->nextPtr; free(tempPtr); } return 0; }
内容的提问来源于stack exchange,提问作者Austin Castro
相关产品推荐
相关产品推荐

