关于链表以及结构体指针别名的思考

本文介绍了一个简单的单链表实现,包括初始化、插入、删除等核心操作,并通过一个示例程序展示了如何使用这些功能。文章还解释了某些参数使用引用传递的原因。
/*#include "malloc.h"      
#include "stdio.h"      

typedef int DataType;      
typedef struct Node      
{      
    DataType data;      
    struct Node *next;      
}LNode,*PNode,*LinkList;       
     
void InitList(PNode &h);               //这里一定要加引用为什么?      
int ListInsert(PNode &h,int pos,DataType x); //以下四行加不加引用都可以,为什么?这是因为这里的h本身就是指针--实参传递后和形参指向同一个地址      
int ListDelete(PNode &h,int pos,DataType *item);      //最好都给他们加上& 防止歧义。
void DestroyList(PNode &h);      
void TraverseList(PNode h);      //这里没有+是因为没有对h链表里面的东西进行修改!
     
int main()      
{      
    int i;      
    int data[7]={0,1,1,2,3,5,8};     //insert之前先定义一个数组,将数组的东西给insert进去。 
    DataType item;     
    PNode h=NULL;   // 先定义一个指针链表。 h是指针变量。为了防止指针乱指,就先让它指向NULL。
    InitList(h);       //先定义一个初结点。
    for(i=0;i<7;i++)      //一个一个的insert. 
    {      
        if(!ListInsert(h,i+1,data[i]))      //这个是对ListInsert函数的判断! 这里是为了返回一个 1来判断程序。
        {      
            printf("插入失败!/n");      
            return 0;      
        }      
    }      
    printf("/n/n删除前单链表中的元素/n");      
    TraverseList(h);      
    if(!ListDelete(h,7,&item))      
    {      
        printf("error!/n");      
        return 0;      
    }      
    printf("/n/n删除后单链表的数据元素/n");      
    TraverseList(h);      
    DestroyList(h);      
    return 0;      
}      
     
//初始化      
void InitList(PNode &h)         //&h是引用(别名),但是用&h通过函数能改变主函数中h的值使其得到空间分配;如果去点&,直接将h传递过来      
{                              //那么h在此函数中确实初始化成功了,但是不能返回到主函数中,即主函数中的h依然没有分配空间----即此函数调用没用      
    h=(LNode *)malloc(sizeof(LNode));       //初始化链表首先先建立一个表头, 先建立一个h h-next=NULL;
    if(!h)      
    {      
        printf("初始化失败!/n");      
    }      
    h->next=NULL;        
}      
  
//插入数据      
int ListInsert(PNode &h,int pos,DataType x)      
{      
    PNode p=h,q;      
    int i=0;      
	//先找位置
    while (p&&i<pos-1)      
    {      
        p=p->next;      
        i++;      
    }      
    if(!p||i>pos-1)      
    {      
        printf("不能插在此处!/n");      
        return 0;      
    }      
    q=(PNode) malloc (sizeof(LNode));      
    if(!q)      
    {      
        printf("error!/n");return 0;      
    }     
	//insert
    q->data=x;      
    q->next=p->next;      
    p->next=q;   
	return 1;
}      
     
//删除数据      
int ListDelete(PNode &h,int pos,DataType *item)      
{      
    PNode p=h,q;      
    int i=0;      
	//先定位 
    while (p->next&&i<pos-1)      
    {      
        p=p->next;      
        i++;      
    }      
    if(!p->next||i>pos-1)      
    {      
        printf("Error!/n");      
        return 0;      
    }      
	//再删除
    q=p->next;      
    p->next=q->next;      
    *item=q->data;      
    free(q);      
    return 1;      
}      
     
//销毁链表      
void DestroyList(PNode &h)      
{      
    PNode p=h->next;      
    while (h)      
    {      
        p=h;      
        h=h->next;      
        free(p);      
    }      
}      
     
//遍历链表      
void TraverseList(PNode h)      
{      
    PNode p=h->next;      
    while (p)       //循环P是否存在!
    {       
		printf("%d/t",p->data);   
        p=p->next;   
    }   
    printf("/n");   
}*/

链表就是 能让指针指向里面一个结点 然后通过操作,可以进行结点分离,结点内插等等 
好比是一堆排序整齐得吸铁石,你可以随意去除其中一块 或者加入一块



<think> 嗯,用户这次明确要求解释C语言结构体的自引用特性,而且特意提到要"详细解释"。看来用户可能正在学习数据结构的基础知识,或者遇到了链表/树结构的实现问题。 注意到用户提供了两份站内引用素材,但内容有些混乱。引用[1]的表述存在语病(比如"不再需要用,去定义第二个变量"这种不通顺的句子),引用[2]则是基础定义。这说明用户可能已经查阅过资料但没找到清晰解释,所以才来追问。 需要重点解释两个核心难点:一是为什么需要自引用(链表实现场景),二是如何处理"不完整类型"这个语法特性。用户大概率会被结构体内嵌自身指针的写法困惑,比如struct Node *next;这种看起来"未定义就使用"的特殊情况。 上次回答数据结构时用了代码示例效果不错,这次也应该采用类似方式。但要注意区分:链表的一级指针实现是最基础版本,二叉树需要展示二级指针嵌套,而包含其他结构体的"互引用"情况虽然不算严格自引用,但实际开发中经常伴随出现需要说明。 用户没有要求对比其他语言,所以专注C语言特性即可。不过最后可以提一句"这种模式在面向对象语言中对应什么概念",作为延伸思考点。另外要强调typedef的最佳实践,因为很多教材错误地直接使用struct关键字声明变量。 </think> C语言结构体自引用是指在结构体内部包含指向**自身类型**的指针成员,这是实现链表、树等动态数据结构的基础。下面通过语法解析和示例详细说明: --- ### **核心概念解析** 1. **语法要求** 结构体自引用**必须使用指针**: ```c struct Node { int data; struct Node* next; // ✅ 正确:指针允许"不完整类型" }; ``` **错误写法**: ```c struct Node { int data; struct Node next; // ❌ 编译错误:结构体大小无法确定(无限递归) }; ``` *原因:结构体大小需在编译时确定,直接嵌套自身会导致无限循环计算大小* 2. **不完整类型(Incomplete Type)** - `struct Node* next;` 中的 `struct Node` 此时是**不完整类型**(编译器仅知其存在,未知细节) - C标准允许用指针指向不完整类型(指针大小固定) --- ### **典型应用场景** #### 1. 单向链表实现 ```c typedef struct ListNode { // 使用typedef简化 int val; struct ListNode* next; // 自引用指针 } ListNode; // 创建链表节点 ListNode* createNode(int val) { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); newNode->val = val; newNode->next = NULL; return newNode; } ``` #### 2. 二叉树节点 ```c typedef struct TreeNode { int data; struct TreeNode* left; // 左子节点指针 struct TreeNode* right; // 右子节点指针 } TreeNode; ``` --- ### **特殊场景:相互引用** 当两个结构体需要互相引用时,需**前置声明**: ```c struct B; // 前置声明 struct A { int value; struct B* b_ptr; // 指向B的指针 }; struct B { char id; struct A* a_ptr; // 指向A的指针 }; ``` --- ### **语法要点总结** | **情景** | **正确写法** | **错误原因** | |-------------------|----------------------------|--------------------------| | 单结构体自引用 | `struct Node* next;` | 避免无限大小计算 | | 多结构体互相引用 | 前置声明 + 指针成员 | 解决编译顺序依赖 | | 别名简化 | `typedef` + 自引用指针 | 提升代码可读性 | > 关键点:**自引用必须通过指针实现**,利用指针大小固定(通常4/8字节)的特性绕过类型未完整定义的编译限制[^1][^2]。 --- ### 相关问题 1. 结构体自引用时为什么不能用普通变量而必须用指针? 2. 如何在自引用的结构体中使用`typedef`别名? 3. 结构体自引用与函数指针结合有哪些应用场景? 4. 在二叉树实现中如何处理结构体自引用的内存释放问题? [^1]: C语言结构体基础语法规范 [^2]: ISO/IEC 9899:2018
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值