结构体初阶
一、结构体类型的声明
1、什么是结构?
数组是一组相同类型的元素的集合。
 结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。
2、结构的声明
问题:什么是类型?
   这是我们创造出的一个类型struct Stu。(分号不能丢)
   如果我们用int类型创造一个变量的时候会使用:int a = 10;。意思是:我们使用类型int创造了一个变量a。
struct Stu
{//结构体的相关属性——结构的成员变量
	char name[20];//名字
	int age;//年龄
	char id[20];//学号
};
int main()
{
	struct Stu s;//使用类型struct Stu创造了一个对象s
	return 0;
}
 

   我们拿类型struct Stu创建出对象s的时候,就相当于我们按照图纸盖起了一栋房子。房子里的一个个房间就相当于对象s中各有一部分空间存放name、age和id。
struct Stu
{
	char name[20];//名字
	int age;//年龄
	char id[20];//学号
}s1,s2;
 
s1和s2也是结构体变量,因为在{}外面创建,所以s1和s2还是全局变量。如果要创建局部的结构体变量的话只能按照下面这种方法。
int main()
{//s是局部变量
	struct Stu s;//对象s
	return 0;
}
 
3.结构成员的类型:
结构成员的类型可以是标量(int age = 10;中的age)、
           数组、
           指针,
       甚至是其他结构体。
 比如说:
struct B
{
	char c;
	short s;
	double d;
};
struct Stu
{
	struct B tea;//在这里面有一个结构体成员tea,它的类型是 struct B
	char name[20];
	int age;
	char id[20];
};
 
二、结构体变量的初始化
结构体中放置的是多个成员,和数组初始化一样,结构体初始化用的也是花括号{}。
struct B
{
	char c;
	short s;
	double d;
};
struct Stu
{
	struct B tea;//在这里面有一个结构体成员tea,它的类型是 struct B
	char name[20];
	int age;
	char id[20];
};
int main()
{
	struct Stu s = { {'w', 20 , 3.14}, "张三", 30, "20220214"};
	return 0;
}
 
struct Point
{
	int x;
	int y;
};
struct Node
{
	int data;
	struct Point p;
	struct Node* next;//指针
}n1 = { 10, {4, 5}, NULL };//结构体嵌套初始化,指针next设置为空。
 
三、结构体成员访问
结构体变量访问成员:结构变量的成员是通过2个操作符访问的,分别是.和->。
struct B
{
	char c;
	short s;
	double d;
};
struct Stu
{
	struct B tea;//在这里面有一个结构体成员tea,它的类型是 struct B
	char name[20];
	int age;
	char id[20];
};
int main()
{
	struct Stu s = { {'w', 20 , 3.14}, "张三", 30, "20220214"};
	printf("%c\n", s.tea.c);//w是放在struct B里面的。我们先在s里面找到tea,然后在tea里面找到c。
	printf("%s\n", s.id);//打印学号
	return 0;
}
 
  而->操作符多应用于结构体指针。当我们需要获取s的地址时使用struct Stu* ps = &s;,*说明ps是指针,struct Stu表示ps所指对象是结构体类型。
int main()
{
	struct Stu s = { {'w', 20 , 3.14}, "张三", 30, "20220214"};
	//printf("%c\n", s.tea.c);
	//printf("%s\n", s.id);
	struct Stu* ps = &s;
	printf("%c\n", (*ps).tea.c);//法一:使用ps解引用找到s,即*ps就是s
	printf("%c\n", ps->tea.c);//法二:先用ps找到s,然后在找到tea。
	//因为tea是结构体变量,不是指针,所以tea访问成员变量c用 . 就可以。
	return 0;
}
 
四、结构体传参
写一个函数打印s的内容:
case1:传值访问
使用print1()函数,结构体对象s作为参数。
void print1(struct Stu t)//t用来接收s
{
	printf("%c  %d  %lf  %s  %d  %s\n", t.tea.c, t.tea.s, t.tea.d, t.name, t.age, t.id);
}
int main()
{
	struct Stu s = { {'w', 20 , 3.14}, "张三", 30, "20220214" };//s是局部变量
	print1(s);
	return 0;
}
 
我们把s传给t,那么t就获得s的全部的值,则打印t的内容就是在打印s的内容。
 运行结果:
case2:传址访问
使用print2()函数,结构体对象s的地址&s作为参数。
void print2(struct Stu* ps)//传递的是s的地址,所以形参ps一定是指针形式
{
	printf("%c  %d  %lf  %s  %d  %s\n", ps->tea.c, ps->tea.s, ps->tea.d, ps->name, ps->age, ps->id);
}
int main()
{
	struct Stu s = { {'w', 20 , 3.14}, "张三", 30, "20220214" };//s是局部变量
	print1(s);
	print2(&s);
	return 0;
}
 
运行结果:
 
 函数print1()和print2()两者是一模一样的结果。
  如果我们采用print1()函数进行传值调用,main()函数中的s作为一个对象会在内存中开辟一段空间,t作为形参要能接住实参s传过来的内容,势必要在内存中也开辟一个和实参s一样大内存空间,用于存储来自s的数据。s有多大,t就要有多大;s有多少数据,t就要有多少数据。这在空间和时间上都有一定的浪费,会导致性能的下降。
   反之如果我们采用print2()函数的方法进行传址调用,把s的地址&s传给print2()函数,则会使用指针变量接收。在32位平台上,指针变量占4个字节;在64位平台上,指针变量占8个字节。除此以外没有额外开销,且地址返回后仍然指向s。
   综合比较,传址调用的传参效率更高,但是在print2()函数中修改的内容会使对象s发生相应的改变。传值调用的传参效率慢,但是对t进行的任何操作都不会修改s的值。










