目录
11.6指向结构体类型数据的指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时指针变量的值就是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
指向结构体变量的指针
这里通过一个简单的例子来说明指向结构体变量的指针变量的应用。
指向结构体变量的指针的应用。
#include<stdio.h>
#include<string.h>
int main()
{
struct student{
long num;
char name[20];
char sex;
float score;
};
struct student stu_1;
struct student *p;
p = &stu_1;
stu_1.num = 1234;
strcpy(stu_1.name,"Li");
stu_1.sex = 'M';
stu_1.score = 89.5;
printf("No.:%ld\nname:%s\nsex:%c\nscore:%f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score) ;
printf("No.:%ld\nname:%s\nsex:%c\nscore:%f\n",(*p).num,(*p).name,(*p).sex,(*p).score) ;
return 0;
}
//输出结果:
No.:1234
name:Li
sex:M
score:89.500000
No.:1234
name:Li
sex:M
score:89.500000
//可见 两个运行结果是相同的
注意,*p两侧的括号不可省略,因为成员运算符“ . ”优先于“ * ”运算符,*p.num就等价于 *(p.num)了。
为了使用方便和使之直观,可以把*p.num改用p->num来代替,它表示p所指向的结构体变量中的num成员。同样的, ( *p).name等价于p->name,也就是说,下列3种关系等价:
-
结构体变量.成员名
-
(*p).成员名
-
p->成员名
上面程序中最后一个printf函数中的输出项表列可以改写为:
p->num,p->name,p->sex,p->score
其中 -> 称为指向运算符。
分析以下几种运算:
p->n; //得到p指向的结构体变量中的成员n的值
p->n++; //得到p指向的结构体变量中的成员n的值,用完该值后使它加1.
++p->n; //得到p指向的结构体变量中的成员n的值加1,然后再使用它。
指向结构体数组元素的指针
指向结构体数组元素的指针的应用。
#include<stdio.h>
struct student{
long num;
char name[20];
char sex;
int age;
};
struct student stu[3] = {
{10101,"Li-1",'M',18},
{10102,"Li-2",'M',19},
{10103,"Li-3",'F',20}
};
int main()
{
struct student *p;
printf("No. Name sex age\n");
for(p=stu;p<stu+3;p++)
{
printf("%5d%7s%7c%8d\n",p->num,p->name,p->sex,p->age);
}
return 0;
}
程序分析:
p是指向struct student结构体类型数据的指针变量。在for语句中先使p的初值为stu,也就是数组第一个元素的起始地址。在第一次循环中输出stu[0]的各个成员的值。然后执行p++,使p自加1。p自加1意味着p所增加的值为结构体数组stu的一个元素所占字节数(在本例代码中为2+20+1+2=25字节)。执行p++后p的值等于stu+1,p指向stu[1]。在第二次循环中输出stu[1]的各成员值。在执行p++后,p的值等于stu+2,再输出stu[2]的各成员值。之后执行p++,p的值变为stu+3,已经不再小于stu+3了,不再执行循环。
注意以下两点:
-
如果p的初值为stu,即指向第一个元素,则p加1后p就指向下一个元素。例如:
(++p)->num : 先使p自加1,然后得到它指向的元素中的num成员值。(即10102)
(p++)->num : 先得到p->num的值(即10101),然后使p自加1,指向stu[1].
(注意两者的不同)
-
程序已定义了p是一个指向struct student类型数据的指针变量,它用来指向一个struct student类型的数据,不应该用来指向stu数组元素的某一成员。
用结构体变量和指向结构体的指针作函数参数
将一个结构体变量的值传递给另一个函数,有以下3种方法。
(1)用结构体变量的成员作参数。例如:用stu[1].num或stu[2].name作函数实参,将参数值传给形参。用法和普通变量作实参是一样的,属于“值传递”方式。应该注意实参和形参的类型保持一致。
(2)用结构体变量作实参。用结构体变量作实参时,采取的也是“值传递”的方式,将结构体变量所占的内存单元的内容全部顺序传递给形参,形参也必须是同类型的结构体变量。在函数调用期间形参也要占用内存单元。这种传递方式在空间和时间上开销较大,如果结构体的规模很大,开销是很可观的。此外,由于采用值传递方式,如果在执行被调用函数期间改变了形参(也就是结构体变量的值),该值不能反回主调函数,这往往造成使用上的不便。因此一般较少使用这种方法。
(3)指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。
有一个结构体变量stu,内含学生学号、姓名和3门课程成绩。要求在main函数中赋值,在另一个函数print中将它们输出。
#include<stdio.h>
#include<string.h>
#define FORMAT "%d\n%s\n%f\n%f\n%f\n"
struct student{
int num;
char name[20];
float score[3];
};
int main()
{
void print(struct student);
struct student stu;
stu.num = 12345;
strcpy(stu.name,"Yang");
stu.score[0] = 77;
stu.score[1] = 68;
stu.score[2] = 89;
print(stu);
return 0;
}
void print(struct student stu)
{
printf(FORMAT,stu.num,stu.name,stu.score[0],stu.score[1],stu.score[2]);
printf("\n");
}
//输出:
12345
Yang
77.000000
68.000000
89.000000
struct student被定义为外部的类型,这样,同一源文件中的各个函数都可以用它来定义变量。main函数中,stu定义为struct student类型变量,print函数中的形参stu也定义为struct student类型变量。在main函数中对stu的各个成员赋值。在调用print函数时,以stu为实参向形参stu实行“值传递”。在print函数中输出结构体变量stu各个成员的值。
将上面的题目改用指向结构体变量的指针作实参。
#include<stdio.h>
#define FORMAT "%d\n%s\n%f\n%f\n%f\n"
struct student{
int num;
char name[20];
float score[3];
}stu={12345,"Yang",77,68,89};
int main()
{
void print(struct student * ); //形参类型修改成指向结构体的指针变量
print(&stu); //实参改为stu的起始地址
return 0;
}
void print(struct student *p) //形参类型修改了
{
printf(FORMAT,p->num,p->name,p->score[0],p->score[1],p->score[2]); //用指针变量调用各个成员的值
printf("\n");
}
//输出:
//12345
//Yang
//77.000000
//68.000000
//89.000000
此程序改用在定义结构体变量stu时赋初值,这样程序可以简化些。print函数中的形参p被定义为指向struct student类型数据的指针变量。注意在调用print函数时,用结构体变量stu的起始地址&stu作实参。在调用函数时将该地址传给形参p(p是指针变量)。这样p就指向了stu。在print函数中输出p所指向的结构体变量的各个成员的值,它们就是stu的成员值。
用指针作函数参数较好,能提高运行效率。
声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》如有错误,欢迎大家指正!