结构体
一、结构体的声明
1.结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。结构体是可以用来描述复杂类型。
ps:数组的概念:是一些同类型元素的集合 记得概念区分开来
2.结构体的声明
struct tag
{
member-list;
}variable-list; //切记不要忘记带分号(;)
逐一分析里面的关键词:
定义方式一:按照定义的格式,定义变量s1
#include<stdio.h>
struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[15];
char name[20];
int age;
}s1;
int main()
{
return 0;
}
定义方式二:在主函数里,定义变量s1
#include<stdio.h>
struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[15];
char name[20];
int age;
};
int main()
{
struct Stu s1;//局部变量
return 0;
}
解析:
如果结构体类型被typedef修饰,那么这个结构体只是相当于被重命名 直接用Stu就行,不要再写struct Stu 创建出来的还是局部变量
#include<stdio.h>
typedef struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[15];
char name[20];
int age;
}Stu;
int main()
{
Stu s1;//局部变量
return 0;
}
3.结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
所以接下来简单介绍下结构体的嵌套
#include<stdio.h>
struct class
{
int grade;
int class;
};
typedef struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[15];
char name[20];
int age;
struct class class; //嵌套结构体
}Stu;
int main()
{
Stu s1;
return 0;
}
4.结构体变量的定义和初始化
1)结构体的初始化
结构体的初始化也有两种和结构体的创建很像
#include<stdio.h>
struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[15];
char name[20];
int age;
}s1 = { 123456, "wudi", 18}; //s1属于第一种初始化
//提醒一下 如果这样创建 s3就是全局变量
struct Stu s3 = { 111111, "yu", 20 };
int main()
{
struct Stu s2 = { 654321, "yao", 19 }; //s2属于第二种初始化
return 0;
}
在一般情况下,肯定是要按顺序的,但是也不是不可以不按顺序,那就是按下面代码那样实现,逐一找到再赋值。
#include<stdio.h>
struct Stu
{
//一个学生信息由 学号+姓名+性别构成
int sno[8];
char name[8];
int age;
};
int main()
{
struct Stu s2 = { .name= "yao",.sno=123456, .age=19 }; //这样
return 0;
printf("%d", s2.age);
}
二、结构体成员访问
1. 结构体变量访问成员
在之前讲过操作符,其中的.就是成员操作符的一种
#include<stdio.h>
struct S
{
char name[8];
int sex; //男生1 女生0
int age;
}s = { "yu" , 0, 18};
int main()
{
printf("%s %d %d", s.name, s.sex, s.age);
return 0;
}
2.结构体的镶嵌
演示代码:
#include<stdio.h>
struct C
{
int grade;
int class;
};
struct S
{
char name[8];
int sex; //男生1 女生0
struct C s;
int age;
};
int main()
{
struct S stu = { "yu", 1,{9,1},18 };
printf("%s %d %d %d %d", stu.name, stu.sex, stu.s.grade, stu.s.class, stu.age);
return 0;
}
运行结果及解析:可以看出结构体中的结构体和没有嵌套的访问差不多,都是用结构体成员访问操作符就可以了
3.易错点
如果在结构体里面创建了整形数组 记得也用大括号 不然就会跟下图一样报错
正确示范:
4.结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。但是通过结构体传值似乎是不能实现,不信?这里马上举个例子
#include<stdio.h>
struct S
{
char name[8];
int sex; //男生1 女生0
int age;
}s1 = { "yu" , 0, 18};
//想通过函数来改变原来的结构体中的变量
void change(struct S s2)
{
s2.sex = 1;
s2.age = 19;
}
int main()
{
change(s1);
printf("%s %d %d", s1.name, s1.sex, s1.age);
}
运行结果:
解析:错误原因也是很简单,大家应该在学结构体之前已经学了函数,函数就是这样,你传过来的只是值,用了形参(实参的一份临时拷贝),你改变的只是形参的值,并没有对是实参本身进行改变。所以这是不可行的。
ps:
只有这样操作才能修改结构体中的字符数组
接下来介绍下这一小节的主角
案例代码:
#include<string.h>
#include<stdio.h>
struct S
{
char name[8];
int sex; //男生1 女生0
int age;
}s1 = { "yu" , 0, 18 };
//想通过函数来改变原来的结构体中的变量
void change(struct S* s2)
{
s2->sex = 1;
s2->age = 19;
strcpy(s2->name, "zhang");
}
int main()
{
change(&s1);
printf("%s %d %d", s1.name, s1.sex, s1.age);
}
注意解析:
当然也可以按照指针的定义访问,这个又回到了上面的结构体成员访问操作符,表达是多种多样了,只要理解了底层逻辑,一切的一切都不是问题。
5.结构体传参方案的对比
请大家先判断下两个传参方式 哪个更好
#include<string.h>
#include<stdio.h>
struct S
{
char name[8];
int sex; //男生1 女生0
int age;
}s1 = { "yu" , 0, 18 };
change1(struct S s2)
{
s2.sex = 1;
s2.age = 19;
strcpy(s2.name, "zhang");
printf("%s %d %d\n", s1.name, s1.sex, s1.age);
}
//想通过函数来改变原来的结构体中的变量
void change2(struct S* s2)
{
(*s2).sex = 1;
(*s2).age = 19;
strcpy((*s2).name, "zhang");
printf("%s %d %d", s1.name, s1.sex, s1.age);
}
int main()
{
change1(s1); //传值
change2(&s1);//传址
return 0;
}
答案是显然易见的,虽然传值更方便理解,更基础一点,但是从代码和内存的角度出发,传址是更好的。传参需要压栈,而传值的时候需要重新拷贝一份这个结构体,如果结构体过大,会导致栈溢出(开销过大),而传地址只需要传进去4/8个字节就可以实现。