关于结构体中的指针变量和指针参数的复杂用法

博主考研时用伪码学习数据结构,想用C语言实现顺序线性表却遇问题。文中介绍了结构体、指针操作、线性表初始化函数等知识,分析了指针参数作用、访问结构体属性方法,还探讨了主函数传参的三种情况及正确写法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在考研过程中用伪码学习的数据结构,本来想用C语言把这些数据结构全部实现一遍,但是一开始就卡再了一个莫名其妙的地方。标题中说的复杂并不是指这种用法多高级,反而是特别基础需要对基础概念特别清晰。

key work:指针、结构体、函数传参、指针类型参数

我想要实现一个顺序表示的线性表,在这本书(严蔚敏的数据结构清华大学出版社)中称为线性表的顺序表示,Sequential Linear List(以下简称SqList)。我觉得在Java中对应的就是ArrayList。赘述一下线性表示,线性表示就是用一组地址连续的存储单元一次存储线性表的数据元素。C语言中有两个东西符合要求,一个是数组(Array)另一个是指针变量。先放一张书的原图: 


这就是书上伪码,就以它为例,阐述一下我遇到的问题

先定义一下结构体

typedef struct 
{
    char* info;
}ElemType;
typedef struct 
{
    ElemType *elem;
    int* temp;
    int length;
    int listsize;
}SqList,*Sq;
1
2
3
4
5
6
7
8
9
10
11
先说关于结构以需要知道的东西:

typedef的作用就是创造了一种新的类型来代表一个或一组已知的类型。所以对于第一个结构体可以认为ElemType就是char*.而第二个结构体则表示有两种变量名可以表示这个结构体,一种是普通类型SqList另一种则是指针类型*Sq(注意这里星号‘*’在前)

  Sqlist name1;
  Sqlist *name2;
  sq name3;
  sq *name4;
1
2
3
4
name1表示这个结构体的实体变量,name2和name3是完全相同的东西都是一个这个结构体的指针。而name4本身就是指针类型再次声明为指针类型,所以它是一个可以指向“这个结构体指针”的指针。所以以下操作都是正确的:

name2 = & name1;
name3 =   name2;
name4 = & name3;
1
2
3
关于指针的操作有两个特殊的运算符*与& 
第一个*有两个使用的地方,一个是在声明指针变量或传参的时候起标识作用,另一个是在获取指针所指的内容的时候,作为运算符起作用

int temp = 2;
int *a = &temp;
*a = 3;
printf(%d\n,temp);
1
2
3
4
运行结果就是temp的值变为了3,第二行的第一个星号*仅仅表示对指针的声明,而第三行第二个星号才表示所指变量的内容。一定要分开来看不然就解释不通了。

然后是关于线性表的一个初始化函数:

void InitList(Sq list){
    //construct a empty sequential list
    printf("here1\n");
    list->elem = (ElemType * )malloc(100*sizeof(ElemType));
    printf("here2\n");
    list->length = 0;
    (*list).listsize = LIST_INIT_SIZE;
}
1
2
3
4
5
6
7
8
首先的一点是关于伪代码中捕捉申请动态空间不成功的部分,不需要考虑

list->elem = (ElemType * )malloc(100*sizeof(ElemType));
if(list->elem == NULL){
    exit(0);
}
1
2
3
4
因为如果没有分配成功,指令台上就会直接显示段错误 从而停止运行,所以就算写了也不会运行。真的不用理。

关于指针参数的作用,一般参数传递机制是复制,也就是说函数中进行的运算不会改变参数。而如果使用指针参数,在函数中修改形式参数的变量时,实际上就是对实际的参数进行的操作。

InitList(Sqlist list){
    ... ...
}
//一般参数
InitList(Sqlist *list){
    //*的第一种用法,标识作用
    ... ...
}
//指针参数
InitList(Sq list){
    //这里虽然没加*,但是Sq本来就在`typedef`的时候别标成了指针
    ... ...
}
//指针参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
第二点要说的是,对于一个指针类型的结构体,想要访问其内部的属性元素时有两种方法:

InitList(Sqlist *list){
    list->elem;
    (*list).elem;
    ... ...
}
1
2
3
4
5
注意(*list).elem 一定要加括号,因为. 的优先级要高于* , 这里需要的是结构体对象所以需要* 还是比较好理解的。而书中伪代码的写法list.elem 是有语法错误的,通不过编译的。

这样写好了函数和参数,但在主函数中如何传参也是很重要的,而且一般书都省略的这部分,我也就是在这出错的。

int main(int argc, char const *argv[])
{
    SqList list;
    Sqlist *list2;
    InitList(&list);
    printf("success!\n");
}
1
2
3
4
5
6
7
对于InitList()的参数,可能有三种添法:

InitList(list);
InitList(list2);
InitList(&list);
1
2
3
第一种看起来像直接传参,但是函数的参量是一个指针类型,所以并不匹配,编译都通过不了。 
第二种,需要的是指针类型,我填的也是指针类型,看起来很对呀。但是我忘了,任何指针类型想要使用必须先初始化,也就是需要分配地址空间。换句话说就是必须需要一个实体类型,所以如果非要这么写,变成下面这样也是对的:

int main(int argc, char const *argv[])
{
    SqList list;
    Sqlist *list2;
    list2 = & list;
    InitList(list2);
    printf("success!\n");
}
1
2
3
4
5
6
7
8
第三种是标准写法,需要的是一个指针类型,而指针类型的本质是储存地址的变量,所以用取地址符& 传给它一个地址是真确的,也是标准的操作方法。

后记: 
这是我第一次写这样的博客,博自己遇到的问题写下来总结出来,为以后自己遇到问题做题个提醒,省的自己“记吃不记打”。这文章还是比较基础的,如果能帮助到看这边文章的你,我也会很开心的。
--------------------- 
作者:Neo33 
来源:优快云 
原文:https://blog.youkuaiyun.com/neo33/article/details/79116671 
版权声明:本文为博主原创文章,转载请附上博文链接!

<think>我们正在讨论C语言中结构体指针的赋值问题。用户的新问题是:如何让一个结构体中的指针变量指向另一个结构体中的指针变量?实际上,这里需要区分两种情况:1.指向另一个结构体指针(即指向另一个结构体实例的指针)2.指向另一个结构体中的指针成员(即指向另一个结构体中某个指针指针)根据用户引用的内容,特别是引用[3]提到了结构体指针的使用,我们可以推断用户可能是在处理结构体之间的关联,比如链表或树等数据结构。因此,我们分两种情况来解答:情况1:结构体A的指针成员指向结构体B的实例(即结构体A的指针成员存储结构体B的地址)情况2:结构体A的指针成员指向结构体B的某个指针成员(即结构体A的指针成员存储的是结构体B中某个指针成员的地址,也就是二级指针)下面分别说明:情况1:指向另一个结构体实例这是最常见的情况,例如在链表中,一个节点的next指针指向下一个节点(另一个结构体实例)。示例代码: ```c#include <stdio.h> #include<stdlib.h>//定义结构体NodestructNode {intdata;structNode *next;//指向另一个Node结构体指针};int main() {//创建两个节点struct Nodenode1={10, NULL};struct Nodenode2= {20,NULL};//让node1的next指针指向node2node1.next =&node2;//通过node1的next指针访问node2的数据printf("node2.data=%d\n",node1.next->data);//输出20return0; }```情况2:指向另一个结构体中的指针成员这种情况较少见,但也是可能的。例如,结构体A中有一个指针成员p,结构体B中有一个指针成员q,我们想让结构体A的p指向结构体B的q(即p存储的是q的地址)。示例代码: ```c#include <stdio.h> #include<stdlib.h>//定义结构体BstructStructB {int*q;// q是一个整型指针}; //定义结构体Astruct StructA {int**p; //p是一个指向指针指针,可以指向另一个结构体中的指针成员}; intmain(){int value=100;structStructBb;b.q =&value;// b的指针成员q指向valuestruct StructA a;a.p= &b.q;//a的指针成员p指向b的指针成员q//通过a.p访问valueprintf("value =%d\n",**a.p);//输出100//注意:a.p的类型是int**,所以先解引用得到q(即&value),再解引用得到valuereturn0;} ```但请注意,情况2中,结构体A的成员p是一个二级指针,它存储的是结构体B中成员q的地址。这样,我们就可以通过p来修改q所指向的内容,甚至修改q本身(让它指向别的地方)。在实际编程中,情况1更为常见,尤其是在构建链表、树等数据结构时。情况2通常用于需要间接修改另一个结构体指针的场景,比如在实现一些复杂的数据结构或内存管理时。总结:1.如果想让结构体A的指针成员指向结构体B的实例,那么直接将结构体B实例的地址赋给结构体A的指针成员即可。2.如果想让结构体A的指针成员指向结构体B中的某个指针成员,那么需要将结构体B中该指针成员的地址(即二级指针)赋给结构体A的指针成员(此时结构体A的指针成员必须是一个二级指针)。注意事项:-在情况1中,要确保被指向的结构体实例的生命周期长于指向它的指针,否则会出现悬垂指针。-在情况2中,要特别注意二级指针的解引用操作,避免错误。引用说明:结构体指针通过访问目标运算符`*`访问对象,特性与其他指针相同[^1]。结构体变量可直接赋值实现成员值传递[^2],但指针成员需要特殊处理以避免浅拷贝问题[^3]。动态分配时需实例化具体对象并初始化值[^4]。§§相关问题§§1.在C语言中,如何避免结构体指针指向无效内存?2.结构体中的二级指针有哪些常见应用场景? 3.如何实现结构体之间指针成员的循环引用?</think>### C语言结构体指针指向另一个结构体指针的详解 在C语言中,让结构体中的指针变量指向另一个结构体中的指针变量,本质上是实现**指针的间接引用**。这需要正确处理指针的层级关系(一级指针或二级指针)。以下是具体实现方法: --- #### 方法1:直接指向另一个结构体指针成员(一级指针) ```c #include <stdio.h> // 定义结构体类型 struct Data { int *ptr; // 指针成员 }; int main() { int value = 100; // 创建两个结构体实例 struct Data source; struct Data target; // 初始化source的指针成员 source.ptr = &value; // 让target的指针成员指向source的指针成员 target.ptr = source.ptr; // 直接赋值指针地址 // 验证结果 printf("Source value: %d\n", *source.ptr); // 输出100 printf("Target value: %d\n", *target.ptr); // 输出100 // 修改验证 *source.ptr = 200; printf("Modified target: %d\n", *target.ptr); // 输出200 return 0; } ``` **关键点:** - 直接通过`target.ptr = source.ptr`实现指针地址复制 - 两个指针共享同一内存地址,修改任一指针会影响另一个 - 适用于需要共享数据的场景[^3] --- #### 方法2:通过二级指针间接引用 当需要指向另一个结构体指针变量本身(而非其指向的值)时: ```c #include <stdio.h> #include <stdlib.h> struct Container { int **pptr; // 二级指针成员 }; int main() { int value = 300; int *ptr = &value; struct Container source; struct Container target; // 初始化source source.pptr = &ptr; // pptr指向ptr的地址 // 让target的二级指针指向source的指针成员 target.pptr = source.pptr; // 复制指针地址 // 三层解引用访问值 printf("Value via target: %d\n", **target.pptr); // 输出300 // 修改指针指向 int new_value = 500; *target.pptr = &new_value; // 修改ptr的指向 printf("Source value now: %d\n", *ptr); // 输出500 return 0; } ``` **关键点:** - 使用二级指针`int **pptr`存储指针变量的地址 - 通过`target.pptr = source.pptr`复制指针地址 - 可修改原始指针的指向(如`*target.pptr = &new_value`) - 适用于需要控制指针本身指向的场景[^1] --- #### 动态内存分配版本 ```c struct Node { int *data_ptr; }; struct Manager { struct Node **node_pptr; // 指向Node指针指针 }; int main() { // 动态创建Node struct Node *node = malloc(sizeof(struct Node)); node->data_ptr = malloc(sizeof(int)); *node->data_ptr = 400; // 创建Manager并指向Node的指针 struct Manager manager; manager.node_pptr = &node; // 存储node的地址 // 通过manager访问Node数据 printf("Data: %d\n", *(*manager.node_pptr)->data_ptr); // 释放内存 free((*manager.node_pptr)->data_ptr); free(*manager.node_pptr); return 0; } ``` --- ### 核心注意事项 1. **指针层级匹配**: - 一级指针指向数据:`int *ptr` - 二级指针指向指针变量:`int **pptr` 2. **生命周期管理**: - 确保被指向的内存有效 - 动态分配内存需配对释放[^4] 3. **访问语法**: - 一级指针:`*ptr` 访问值 - 二级指针:`**pptr` 访问值 4. **应用场景**: - 共享数据:直接复制指针(方法1) - 控制指针指向:使用二级指针(方法2) - 数据结构实现:如链表节点互指[^3] > 引用说明:结构体指针通过访问目标运算符`*`访问对象[^1],结构体变量可直接赋值实现成员值传递[^2],但指针成员需要特殊处理以避免浅拷贝问题[^3]。动态分配时需实例化具体对象并初始化值[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值