The C Programming language(四)结构

结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字下。C语言中典型的例子来自图形领域:点由一对坐标定义,矩形由两个点定义。
(结构可以拷贝、赋值、传递给函数,函数可以返回结构类型的返回值)
例如,采用结构存放这两个坐标,其声明如下:

struct point{
	int x;
	int y;
};

关键字struct引入结构声明,结构声明包含在花括号内的一系列声明组成。struct后面的名字称为结构标记(这里是point),在定义之后,结构标记就代表花括号内的声明。
结构中定义的变量称为成员。(结构成员、结构标记和普通变量(非成员)可以采用相同的名字)

struct 声明定义一种数据类型,在标志结构成员表结束的右花括号之后可以跟一个变量表,这与其他基本类型的变量声明是相同的。
struct {…} x,y,z; 从语法角度,这种方式的声明与声明 int x,y,z;类似,这两个声明都将x、y与z声明为指定类型的变量,并且为它们分配存储空间。如果结构声明的后面不带变量表,则不需要为它分配存储空间,仅仅描述一个结构的模板,在以后定义结构实例时便可以使用该标记定义,例如,对于上面给出结构声明point,语句struct point pt;定义一个struct point 类型pt。
结构的初始化可以在定义后面使用初值表进行,初值表中同每个成员对应的初值必须是常量表达式。
struct point maxpt = {320 ,200};
在表达式中,可以通过下列形式引用某个特定结构的成员:
结构名.成员
例如,可用下列语句打印坐标:printf("%d,%d",pt.x,pt.y);
或者通过下列代码计算原点(0,0)到点pt的距离:

double dist,sqrt(double);
dist = sqrt((double)pt.x * pt.x + (double)pt.y * pt.y);

结构可以嵌套:
在这里插入图片描述

struct rect{
	struct point pt1;
	struct point pt2;
	};

结构rect包含两个point类型的成员,
如果按照下列方式声明screen变量:struct rect screen;
则可以用语句:screen.pt1.x 引用screen的成员pt1的x坐标。

即结构体变量有以下特点:

  • 在定义结构体变量时可以对它的成员初始化
    例如:struct Student b = {.name=“Zhang Fang”};其他未指定初始化的数值型成员初始化为0,字符型成员被系统初始化为’\0‘,指针型成员被系统初始化为NULL
  • 可以引用结构体变量中成员的值
  • 只能对最低级的成员进行赋值或存取以及计算(可以对结构体的变量根据类型进行计算)
  • 同类的结构体变量可以互相赋值
  • 可以引用结构体变量成员的地址,也可以引用结构体变量的地址

例如:输入两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩

#include<stdio.h>
int main()
{
	struct student{
		int num;
		char name[20];
		float core;
	}student1,student2;
	scanf("%d %s %f",&student1.num,student1.name,&student1.core);//由于数组名本身就代表地址,所以无需&取地址符号
	scanf("%d %s %f",&student2.num,student2.name,&student2.core);
	if(student1.core>student2.core)
	printf("%d %s %f",student1.num,student1.name,student1.core);
	else if(student1.core<student2.core)
	printf("%d %s %f",student2.num,student2.name,student2.core);
	else
	{
		printf("%d %s %f",student1.num,student1.name,student1.core);
		printf("%d %s %f",student2.num,student2.name,student2.core);
	}
}

结构体数组
每个数组元素都是一个结构体类型的数据,分别包括各个成员项
例如:有n个学生的信息(包括学号、姓名、成绩),要求按照成绩的高低顺序输出各学生的信息

#include<stdio.h>
struct student{
	int num;
	char name[20];
	float score;
	};
int main()
{
	struct student stu[3]={{10101,"zhang",78},{10103,"li",85.2},{10105,"wang",45.2}};
	struct student temp;
	const n=3;
	int i,j;
	for(i=0;i<n-1;i++)//使用冒泡法,比较相邻值
	{
		for(j=0;j<n-1-i;j++)
		{
			if(stu[j].score>stu[j+1].score)
			{
			temp=stu[j+1];
			stu[j+1]=stu[j];
			stu[j]=temp;
			}
			
		}
	}
	for(i=0;i<n;i++)
	{
		printf("%6d %8s %6.2f",stu[i].num,stu[i].name,stu[i].score);
	}
}

指向结构体变量的指针
一个结构体变量的起始地址就是这个结构体变量的指针,指向结构体对象的指针变量即可指向结构体变量,也可以指向结构体数组中的数据。
为了方便,(*p).num 用 p->num 来代替,p->num表示p所指向的结构体变量中的num成员
即以下三种形式等价:

  • stu.成员名
  • (*p).成员名
  • p ->成员们
    下例用指针变量指向结构体数组的元素:
#include <stdio.h>
struct student{
	int num;
	char name[20];
	float score;
}; 
struct student stu[3] = {{10000,"zhansan",84.5},{10001,"lisi",89.3},{10002,"wangpo",59.9}};
int main(){
	struct student *p;
	for(p=stu;p<stu+3;p++)
	{
		printf("%6d %6s %6.1f\n",p->num,p->name,p->score);
	}
}

注意:程序定义了p是一个指向struct student 类型对象的指针变量,它用来指向一个struct student类型的对象(p的值是stu数组的一个元素的起始地址),不应用来指向stu数组元素中的某一个成员。
例如:以下用法是不正确的 p=stu[1].name;
如果要将某一成员的地址赋给p,可以用强制类型转换,先将成员的地址转换成p的类型
p=(struct student *)stu[0].name;
此时,p的值是stu[0]元素的name成员的起始地址
可以用printf("%s",p);输出stu[0]中成员name的值
如果执行printf("%s,p+1);则输出stu[1]中name的值
执行p++时,p的值增加了结构体struct student的长度

将一个结构体变量的值传递给另一个函数

  • 用结构体变量的成员作参数,用法和用普通变量作实参是一样。
  • 用结构体变量作实参,将结构体变量所占的内存单元的内容全部按顺序传递给形参
  • 用指向结构体变量(或数组元素)的指针作实参,将地址传给形参

下例分别用3个函数实现不同的功能:

  • 用input函数来输入数据
  • 用max函数找出成绩最高的学生
  • 用print函数来输出成绩最高学生的信息
#include <stdio.h>
struct student{
	int num;
	char name[20];
	float score;
}; 
int main(){
	void input(struct student stu[]);
	struct student max(struct student stu[]);
	void print(struct student stu);
	struct student stu[3];
	struct student *p;
	p=stu;
	input(p);
	print(max(p));
	return 0;
}

void input(struct student stu[])
{int i;
for(i=0;i<3;i++)
	scanf("%d%s%f",&stu[i].num,&stu[i].name,&stu[i].score);
}

struct student max(struct student stu[]){
	int i,m=0;
	for(i=0;i<3;i++)
	if(stu[i].score>stu[m].score)
	m=i;
	return stu[m];
}

void print(struct student stud)
{
	printf("%d%s%6.1f",stud.num,stud.name,stud.score);
}

注意:用max§的值作为实参调用print函数,print函数的形参stud是struct student 类型的变量(而不是数组),在调用时,将stu[m]的值传递给形参stud,这时传递的不是地址,而是结构体变量中的信息。

用指针处理链表
链表是动态地进行存储分配的一种结构,用数组存放数据时,必须事先定义固定的数组长度(需要将数组定得足够大但是可能会浪费内存),而链表根据需要开辟内存单元。
链表有一个头指针变量,用head表示,存放一个地址,该地址指向一个元素,链表中每一个元素称为结点,每一个节点都包括两部分:(1)用户需要用的实际数据;(2)下一个节点的地址。最后一个元素的地址存放一个NULL,链表到此结束。

静态链表:
所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放。

#include<stdio.h>
struct student
{
	int num;
	float score;
	struct student *next;
};
int main()
{
struct student a,b,c,*head,*p;
a.num=10101;a.score=89.5;
b.num=10103;b.score=90;
c.num=10105;c.score=85.3;
head=&a;
a.next=&b;
b.next=&c;
c.next=NULL;
p=head;
do
{printf("%ld%5.1f\n",p->num,p->score);
p=p->next;
}
while(p!=NULL);
return 0;
}

动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个个开辟结点和输入各结点数据,并建立起前后的关系。

#include<stdio.h>
#include<stdlib.h>
#define LEN sizeof(struct student)
struct student
{
int num;
float score;
struct student *next;
};
int n;
struct student *creat(void)
{
struct student *head;
struct student *p1,*p2;
n=0;
p1=p2=(struct student *)malloc(LEN);
scanf("%d,%f",&p1->num,&p1->scor
e);
head=NULL;//当没有分配内存,即链表为空时的情况
while(p1->num!=0)//如果输入的p1->num不等于0,则输入的是第1个结点数据,令head=p1,使head指向新开辟的结点
{
	n=n+1;
	if(n==1)
		head=p1;//p1所指的结点作为第一个结点
	else
		p2->next=p1;//将p1所指的结点连接到表位
	p2=p1;
	p1=(struct student *)malloc(LEN);
	scanf("%d,%f",&p1->num,&p1->score);
	p2->next=NULL;
	return(head);
}
int main()
{
struct student *pt;
pt = creat();
printf("%d,%5.1f\n",pt->num,pt->score);
return 0}

共用体union:
同一个内存单元存放不同类型的变量。例如,将一个短整型变量、一个字符型变量放在同一个地址开始的内存单元中。
(1)同一个内存段可以用来存放不同类型的成员,但在每一瞬间只能存放其中一个成员,而不是同时存放几个
(2)可以对共用体初始化,但初始化表只能有一个常量

union Data
{int i;
char ch;
float f;
}a={1,'a',1.5};//不能初始化3个成员
union Data a={16};
union Data a={.ch='j'};

(3)共用体变量中起作用的成员使最后一次被赋值的成员
(4)共用体变量的地址和成员的地址都是相同的
(5)不能对共用体变量名赋值,也不能引用变量来得到一个值,允许同类型的共用体变量互相赋值

typedef声明新类型名
typedef指定新的类型名来代替已有的类型名(并没有创建一个新类型,只是为某个存在的类型增加了一个新的名称而已)

发布了54 篇原创文章 · 获赞 4 · 访问量 1010

猜你喜欢

转载自blog.csdn.net/buzhiquxiang/article/details/103906751