结构指针的使用
指针类型变量:
指针类型,是变量类型的一种,它是专门用来存储变量的地址的。
例如 int *p; 表示p是一个指针变量,它用来存储某个整型变量的地址。
int a=5;
int *p=&a;
这样,就将整型变量a的地址存储在了p中。
如图示所示:
变量a中存储的是5,它自身的地址是 0x001A
变量p中存储的是0x001A,也就是a的地址,它自身的地址是0x10AB
下面是一个结构指针:
//定义结点数据类型:
typedef struct
{
float coef;
int expn;
}term, ElemType;
//定义结点,及指向结点的指针
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
这里,定义了一个链表的结点,它的数据项包括 coef, expn,就是系数和指数。还包括一个指向下一个结点的指针。
给这个结构定义了两个别名:
一个是LNode,用来代替struct LNode
还有一个是LinkList,是指向结点的指针,用来代替 struct LNode *
如果定义了这样一个变量p: LinkList p;
那么它就是一个指向结点的指针,它在内存中的存储可以用下图表示:
这个指针变量p是用来存储结构型变量的地址的。
具体说就是这种结构型变量的地址:struct LNode别名为LNode。现在,它的值是空的,因为还没有为它赋值。指针变量本身也需要占用内存空间,因此,指针变量p本身的内存地址是0xA01C。
现在,定义一个结构型变量:
LNode n1; 假设它的地址是 0x013B
给这个结构型变量赋值:
n1.coef=1.5;
n1.expn=2;
n1.next=NULL
那么它在内存中的存储情况如图:
之后,把结点n1的地址赋值给指针 p
p=&n1; &是取地址符号
当使用*p时候,*称为引用符号,就是对结构型变量n1的引用,就是顺着p存储的地址0x013B找到结构型变量n1,并使用它。例如,(*P).coef 是1.5 (*p).expn是2。
还有一种使用结构指针的方法,就是用 ->代替 (*).因为前者意义明确,书写简洁。可以写成这样:p->coef 或 p->expn
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
float coef;
int expn;
}term, ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
main()
{
LNode n1;
LinkList p;
n1.data.coef = 1.5;
n1.data.expn = 2;
n1.next = NULL;
p=&n1;
printf("coef=%f expn=%d \n",(*p).data.coef, (*p).data.expn);
}
指针作为参数传递给函数:
一种情况是,传递指针的拷贝。即指针作为实参,被调用函数中是形参。形参就像临时变量一样,当函数调用结束后就自动销毁掉了。
例如,有这样一个结构体变量n1,它在内存中的地址是0x013B,它包括两个数据成员coef=1.5,expn=2,和一个指向下一个结点的指针next=NULL。
有一个指针变量p它的地址是0xA01C,将结构体变量n1的地址赋值给p
p=&n1; 此时,p的值为0x013B。
现在,想要在一个函数func中打印结构体n1的数据,那么指针p的值将作为实参,拷贝一份给形参pf。注意,这里实参p和形参pf的地址是不一样的。
在func函数中可以引用pf中的地址值,找到结构体n1的位置,从而打印数据。
如图所示:
示例程序:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
float coef;
int expn;
}term, ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
void func(LinkList pf)
{
printf("coef=%f expn=%d \n",(*pf).data.coef, (*pf).data.expn);
}
main()
{
LNode n1;
LinkList p;
n1.data.coef = 1.5;
n1.data.expn = 2;
n1.next = NULL;
p=&n1;
func(p);
}
还有一种情况:
如果需要改变指针本身的值,就不能只是传递指针的拷贝,而是需要传递指针的地址:
例如,定义了两个结构体结点n1,n2。如果需要在函数func中将指针p的值修改为n2的地址,那么就需要传递指针的地址过去,如果只是修改形参的值,不会对实参p起到任何影响,如图所示:
如何在函数func中修改实参p的值为0x015F,使它指向结点n2呢?那就需要在给func传递参数的时候,传递的是p的地址:func(&p)。&是取地址符号,也就是传递的是0xA01C,之后在func中对此地址进行引用,就能修改p的值了。
但是这时,形参的类型不再是LinkList型了,而是 LinkList *,也就是指向指针的指针,也就是一个二级指针,如果不用别名,而是写完整的话,应该是:
func(struct LNode **pf ) 用定义好的别名简写成 func(LinkList * pf)
pf是一个指针,它存储的是某种数据类型的地址。
哪种数据呢?是一个指针类型。
指向什么的指针呢?指向结构struct LNode的指针。
对pf的引用,首先找到p,即*pf
再对p引用,又找到n2,即*(*pf)
因此(**pf).data.coef 应该是 2.5 (**pf).data.expn 应该是3
如图所示:
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
float coef;
int expn;
}term, ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
LNode n1,n2;
//在函数中修改指针所指向的值
void func(LinkList *pf)
{
*pf=&n2;
printf("在函数func中打印 coef= %f expn = %d\n",(**pf).data.coef, (**pf).data.expn);
}
main()
{
LinkList p;
n1.data.coef = 1.5;
n1.data.expn = 2;
n1.next = NULL;
n2.data.coef = 2.5;
n2.data.expn = 3;
n2.next = NULL;
//是p指向 结点 n1
p=&n1;
//打印其值
printf("coef=%f expn=%d\n",p->data.coef , p->data.expn );
//改变指针p的指向
printf("改变p值后\n");
func(&p);
//打印其值
printf("在主函数中打印\n");
printf("coef=%f expn=%d\n",p->data.coef , p->data.expn );
}