天天看点

[C]第七章--结构体什么是结构体?结构的声明结构体变量的定义和初始化结构体成员的访问结构体传参结构体的嵌套

结构体

  • 什么是结构体?
  • 结构的声明
  • 结构体变量的定义和初始化
    • 定义
    • 初始化
  • 结构体成员的访问
  • 结构体传参
  • 结构体的嵌套

什么是结构体?

  • 当单独的几个数据类型不足以表达用户需求时,这时候就需要构建比较复杂的结构体变量.
  • 结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。

    结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。

  • 结构,是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同的类型.

结构的声明

struct tag{
	member-list;
}variable-list;
           

struct

是自定制的类型,用这个类型来抽象信息.

什么又是抽象?

  • 是在具体事物的基础上,提取出一些需要的核心信息,再表达出来
  • 抽象相对于具体,少了很多"信息"

这里定义好了结构体变量

Student

,但是在实际使用中都是以

struct Student xx

的 形式出现,十分麻烦,所以我们通常在定义时加上关键词

typedef

:

例如描述一个学生,他有名字,年龄,性别,学号:

typedef struct Student{
	char name[20];	//名字
	int age;		//年龄
	char sex[5];	//性别
	char id[20];	//学号
}Stu;	
//注意 这里最后的分号不能省略
           

通过

typedef

关键词的使用,之后结构体变量的使用就简化为

Student xx

最后一行的

Stu

,与

typedef

相结合就可以理解为重命名,所以之后就可以使用

Stu xx

,并且将变量扩展为全局变量.

同样这一全局变量的设定也可以单独语句实现:

typedef struct Student Stu;
           
  • 全局变量的设定会少写很多

    struct

    ,但是会有重名问题的风险.
    结构的成员可以是标量,数组,指针,甚至是其他结构体.
               

结构体变量的定义和初始化

定义

struct Point{
	int x;
	int y;
}p1;			//声明类型的同时定义变量p1
struct Point p2; 	//定义结构体变量p2
           

初始化

struct Point p3 = { x , y };

struct Stu{		//类型声明
	char name[15];
	int age;
};
struct Stu s = {"zhangsan" , 20};	//初始化

struct Node{
	int data;
	struct Point p;
	struct Node* next;
}n1 = {10,{4,5},NULL};		//结构体嵌套初始化 	
           

结构体成员的访问

  • .

    操作符: 访问结构体成员变量
  • ->

    操作符: 结构体指针访问指向变量的成员
struct S s;
strcpy(s.name , "zhangsan");	//使用 . 访问name成员
s.age = 20;		//使用 . 访问age成员
           
struct Stu{
	char name[20];
	int age;
};

void print(struct Stu* ps){
	printf("name = %s  age = %d\n",(*ps).name,(*ps).age);
	printf("name = %s  age = %d\n",ps->name,ps->age);
}

int main(){
	struct Stu s = {"zhangsan",20};
	print(&s);		//结构体地址传参
	return 0;
}
           

从二者相同的结果中分析我们可以得到结论:

  • a -> b

    等效于

    (*a).b

结构体传参

struct S{
	int data[1000];
	int num;
};
struct S s = {{1,2,3,4},1000};


//结构体传参
void print1(struct S s){
	printf("%d\n",s.num);
}


//结构体地址传参
void print2(struct S* ps){
	printf("%d\n",ps->num);
}

int main(){
	print1(s);		//传结构体
	print2(&s);	//传地址
	return 0;
}
           

我们可以想想上面代码中

print1

print2

那个函数更好?

答案是:

print2

函数

原因比较复杂,简单总结为一下一段话:

讲解函数栈帧的时候,我们讲过函数传参的时候,参数是需要压栈的.如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降.
  • 结论:

    结构体传参的时候,推荐传递结构体地址,这样可以降低开销.

结构体的嵌套

typedef struct School{
	Student students[1000];	//之前定义过的结构体变量
	int size;
}School;
           
  • 这样的话不能直接嵌套自身这个结构体,如果嵌套了运行结果为

    使用了未定义的结构体

    .
  • 而且也无法对这个结构体函数的变量进行

    sizeof

    操作,因为不知道结构体的大小(内存占存大小)

    如果想要嵌套,可以用指针来实现:

typedef struct Student{
	char name[1024];
	int score;
	struct Student* s;	//指针 ,嵌套了自身结构体
}student;
           
  • 这样的话,如果执行

    sizeof

    这个结构体函数定义的变量,就会有明确的占存大小

    例如上面这个程序:

    sizeof的结果为: 1024 + 4 + 4 = 1032;

嵌套也可能很复杂,多个结构体变量可以互相传参,这就会产生占存动辄上G的结构体,所以谨慎使用嵌套.