C语言自定义数据类型(三)结构体指针

所谓结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么,这个指针变量就指向该结构体变量。

目录


一、指向结构体变量的指针

1.1举例说明

指向结构体对象的指针变量既可指向结构体变量,也可指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。例如:

struct Student* pt;        //pt可以指向struct Student类型的变量或数组元素

先通过一个例子了解什么是指向结构体变量的指针变量以及怎样使用它。

举例:通过指向结构体变量的指针变量输出结构体变量中成员的信息。

解题思路:在已有的基础上,本题要解决两个问题:(1)怎样对结构体变量成员赋值;(2)怎样通过指向结构体变量的指针访问结构体变量中成员。

#include <stdio.h>
#include <string.h>
int main()
{
	struct Student					//声明结构体类型struct Student
	{
		long num;
		char name[20]; char sex;
		float score;
	};
	struct Student stu_1;			//定义struct Student类型的变量stu_1
	struct Student* p;				//定义指向struct Student 类型数据的指针变量p
	p = &stu_1;						//p指向stu_1
	stu_1.num = 1001;				//对结构体变量的成员赋值
	strcpy_s(stu_1.name, "Li Ming");//用字符串复制函数给stu_1.name赋值
	stu_1.sex = 'M';
	stu_1.score = 95;
	printf("No.:%ld\nname :%s\nsex :%c\nscore :%5.1f\n", stu_1.num, stu_1.name, stu_1.sex, stu_1.score);
	printf("\nNo.:%ld\nname :%s\nsex :%c\nscore :%5.1f\n", (*p).num, (*p).name, (*p).sex, (*p).score);
	return 0;
}

运行结果:

程序分析:

两个 printf 函数输出的结果是相同的。在主函数中声明了 struct Student 类型,然后定义一个 struct Student 类型的变量 stu_1。又定义一个指针变量 p,它指向一个 struct Student 类型的对象。将结构体变量 stu_1 的起始地址赋给指针变量 p,也就是使 p 指向 stu_1。然后对 stu_1 的各成员赋值。

第 1 个 printf 函数是通过结构体变量名 stu_1 访问它的成员,输出 stu_1 的各个成员的值。stu_1.num 表示 stu_1 中的成员 num,依此类推。第 2 个 printf 函数是通过指向结构体变量的指针变量访问它的成员,输出 stu_1 各成员的值,使用的是 (*p).num 这样的形式。(*p) 表示 p 指向的结构体变量,(*p).num 是 p 所指向的结构体变量中的成员 num。注意 *p 两侧的括号不可省,因为成员运算符 “.” 优先于 “ * ” 运算符,*p.num 就等价于 *(p.num) 了。

说明:

为了使用方便和直观,C 语言允许把 (*p).num 用 p->num 代替,“->”代表一个箭头,p->num 表示 p 所指向的结构体变量中的 num 成员。同样,(*p).name 等价于 p->name。“->” 称为指向运算符。

如果 p 指向一个结构体变量 stu,以下 3 种用法等价:

①stu.成员名        (如stu.num);
②(*p).成员名       (如(*p).num);

③p->成员名        (如p->num)。

二、指向结构体数组的指针

2.1举例说明

可以用指针变量指向结构体数组的元素。请分析下面的例子。

举例:有 3 个学生的信息,放在结构体数组中,要求输出全部学生的信息。

解题思路:用指向结构体变量的指针来处理:

(1)声明结构体类型 struct Student,并定义结构体数组,同时使之初始化;

(2)定义一个指向 struct Student 类型数据的指针变量p;

(3)使p指向结构体数组的首元素,输出它指向的元素中的有关信息;

(4)使p指向结构体数组的下一个元素,输出它指向的元素中的有关信息;

(5)再使p指向结构体数组的下一个元素,输出它指向的元素中的有关信息。

#include<stdio.h>
struct Student					//声明结构体类型struct Student
{
	int num;
	char name[20];
	char sex;
	int age;
};
struct Student stu[3] = { {1001,"Li Ming" , 'M',18},{1002,"Zhang San" ,'M',19},{1003,"Wang Wu",'F',20} }; //定义结构体数组并初始化
int main()
{
	struct Student* p;			//定义指向struct Student结构体变量的指针变量
	printf(" No.Name                    sex age\n");
	for (p = stu; p < stu + 3; p++)
		printf("%5d %-20s %2c %4d\n", p->num, p->name, p->sex, p->age);//输出结果
	return 0;
}

运行结果:

程序分析:

p 是指向 struct Student 结构体类型数据的指针变量。在 for 语句中先使 p 的初值为 stu,也就是数组 stu 中序号为 0 的元素(即 stu[0] )的起始地址。在第 1 次循环中输出 stu[0] 的各个成员值。然后执行 p++,使 p 自加 1。p 加 1 意味着 p 所增加的值为结构体数组 stu 的一个元素所占的字节数(在 Visual C++ 环境下,本例中一个元素所占的字节数理论上为 4+20+1+4 = 29 字节,实际分配 32 字节)。执行 p++ 后 p 的值等于 stu+1,p 指向stu[1]。在第 2 次循环中输出 stu[1] 的各成员值。在执行 p++ 后,p 的值等于 stu+2,再输出 stu[2] 的各成员值。在执行 p++ 后,p 的值变为 stu+3,已不再小于 stu+3 了,不再执行循环。

2.2注意

(1)如果 p 的初值为 stu,即指向 stu 的序号为 0 的元素,p 加 1 后,p 就指向下一个元素。

例如:

(++p)->num        先使p自加1,然后得到 p 指向的元素中的 num 成员值(即1002)
(p++)->num        先求得 p->num的值(即1001),然后再使 p 自加 1,指向 stu[1] 请注意以上二者的不同。

(2)程序定义了 p 是一个指向 struct Student 类型对象的指针变量,它用来指向一个 struct Student 类型的对象,不应用来指向 stu 数组元素中的某一成员。

例如,下面的用法是不对的:

p = stu[1].name;    // stu[1].name是stu[1]元素中的成员name的首字符的地址

编译时将给出 “ 警告 ” 信息,表示地址的类型不匹配。不要认为反正 p 是存放地址的,可以将任何地址赋给它。如果一定要将某一成员的地址赋给 p,可以用强制类型转换,先将成员的地址转换成 p 的类型。例如:

p = (struct Student*)stu[0].name;

此时,p 的值是 stu[0] 元素的 name 成员的起始地址。可以用 “ printf (“%s”, p); ” 输出 stu[0] 中成员 name 的值。但是,p 仍保持原来的类型。如果执行 “ printf( “%s”,p+1); ”,则会输出 stu[1] 中 name 的值。执行 p++ 时,p 的值的增量是结构体 struct Student 的长度。

三、用结构体变量和结构体变量的指针作函数参数

3.1引入

将一个结构体变量的值传递给另一个函数,有 3个方法:

(1)用结构体变量的成员作参数。例如,用 stu[1].num 或 stu[2].name 作函数实参,将实参值传给形参。用法和用普通变量作实参是一样的,属于“值传递”方式。应当注意实参与形参的类型保持一致。

(2)用结构体变量作实参。用结构体变量作实参时,采取的也是 “ 值传递 ” 的方式,将结构体变量所占的内存单元的内容全部按顺序传递给形参,形参也必须是同类型的结构体变量。在函数调用期间形参也要占用内存单元。这种传递方式在空间和时间上开销较大,如果结构体的规模很大时,开销是很可观的。此外,由于采用值传递方式,如果在执行被调用函数期间改变了形参(也是结构体变量)的值,该值不能返回主调函数,这往往造成使用上的不便。因此一般较少用这种方法。

(3)用指向结构体变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参。

3.2举例说明

举例:有 n 个结构体变量,内含学生学号,姓名和 3 门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3 门课程成绩和平均成绩)。

解题思路:将 n 个学生的数据表示为结构体数组(有 n 个元素)。按照功能函数化的思想,分别用 3 个函数来实现不同的功能:(1)用input函数来输入数据和求各学生平均成绩。(2)用max函数来找平均成绩最高的学生。(3)用print函数来输出成绩最高学生的信息。在主函数中先后调用这3个函数,用指向结构体变量的指针作实参。最后得到结果。

#include<stdio.h>
#define N 3				//学生数为3
struct Student			//定义结构体类型
{
	int num;
	char name[20];
	float score[3];
	float aver;
};

int main()
{
	void input(struct Student stu[]);			//函数声明
	struct Student max(struct Student stu[]);	//函数声明
	void print(struct Student stu);				//函数声明
	struct Student stu[N];						//定义结构体数组
	struct Student* p = stu;					//定义结构体指针
	input(stu);									//调用input函数
	print(max(stu));							//调用print函数,以max函数的返回值作为实参
	return 0;
}

void input(struct Student stu[])				//input函数
{
	printf("请输入各学生的信息:学号,姓名,3门课成绩\n");
	for (int i = 0; i < N; i++)
	{
		scanf_s("%d %s %f %f %f", &stu[i].num, stu[i].name, 20, &stu[i].score[0], &stu[i].score[1], &stu[i].score[2]);
		stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0;
	}
}

struct Student max(struct Student stu[])		//max函数
{
	int k = 0;
	for (int i = 1; i < N; i++)					//找出平均值最大的学生
	{
		if (stu[i].aver > stu[k].aver)
			k = i;
	}
	return stu[k];
}

void print(struct Student stu)					//print函数
{
	printf("\n成绩最高的学生是:\n");
	printf("学号:%d\n姓名:%s\n三门课成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",
		stu.num, stu.name, stu.score[0], stu.score[1], stu.score[2], stu.aver);
}

运行结果:

程序分析:

(1)结构体类型 struct Student 中包括 num(学号),name(姓名),数组score( 3 门课成绩)和 aver (平均成绩)。在输入数据时只输入学号,姓名和 3 门课成绩,未给 aver 赋值。aver 的值是在 input 函数中计算出来的。

(2)在主函数中定义了结构体 struct Student 类型的数组 stu 和指向 struct Student 类型数据的指针变量 p,使 p 指向 stu 数组的首元素 stu[0]。在调用 input 函数时,用指针变量 p 作为函数实参, input 函数的形参是 struct Student 类型的数组 stu (注意形参数组 stu 和主函数中的数组 stu 都是局部数据,虽然同名,但在调用函数进行虚实结合前二者代表不同的对象,互相间没有关系)。在调用 input 函数时,将主函数中的 stu 数组的首元素的起始地址传给形参数组 stu,使形参数组 stu 与主函数中的 stu 数组具有相同的地址。因此在 input 函数中向形参数组 stu 输入数据就等于向主函数中的 stu 数组输入数据。

在用 scanf_s 函数输入数据后,立即计算出该学生的平均成绩,stu[i].ave r代表序号为 i 的学生的平均成绩。input 函数无返回值,它的作用是给 stu 数组各元素赋予确定的值。

(3)在主函数中调用 print 函数,实参是 max(p)。其调用过程是先调用 max 函数(以 p 为实参),得到 max(p) 的值(此值是一个 strct Student 类型的数据)。然后用它调用print函数。

现在先分析调用 max 函数的过程:与前相同,指针变量 p 将主函数中的 stu 数组的首元素的起始地址传给形参数组 stu,使形参数组 stu 与主函数中的 stu 数组具有相同的地址。在 max 函数中对形参数组的操作就是对主函数中的 stu 数组的操作。在 max 函数中,将各人平均成绩与当前的 “最高平均成绩” 比较,将平均成绩最高的学生在数组 stu 中的序号存放在变量 m 中,通过 return 语句将 stu[m] 的值返回主函数。请注意:stu[m] 是一个结构体数组的元素。max 函数的类型为 struct Student 类型。

(4)用 max(p) 的值(是结构体数组的元素)作为实参调用 print 函数。print 函数的形参 stu 是  struct Student 类型的变量(而不是 struct Student 类型的数组)。在调用时进行虚实结合,把 stu[m] 的值(是结构体元素)传递给形参 stu ,这时传递的不是地址,而是结构体变量中的信息。在 print 函数中输出结构体变量中各成员的值。

(5)以上 3 个函数的调用,情况各不相同:

①调用 input 函数时,实参是指针变量 p,形参是结构体数组,传递的是结构体元素的起始地址,函数无返回值。

②调用 max 函数时,实参是指针变量 p,形参是结构体数组,传递的是结构体元素的起始地址,函数的返回值是结构体类型数据。

③调用 print 函数时,实参是结构体变量(结构体数组元素),形参是结构体变量,传递的是结构体变量中各成员的值,函数无返回值。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年12月11日
下一篇 2023年12月11日

相关推荐