目录
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/7be6394d418208402417da5b2d103e80.gif)
导语
在C语言中,有很多的类型,例如int,short,long,char,double,float等等,它们属于内置类型,除开这些,C语言也允许我们自定义类型,我们今天要学习的就是自定义类型中的一个重要的知识点,结构体。
1. 结构体类型的声明
1.1 基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以由不同的类型组成
1.2 结构的声明
我们可以用结构体来描述一个学生,一个学生的组成成员有哪些呢?
我们知道,有姓名,性别,年龄,学号等,各个成员组合起来构成了结构体。
struct student
{
//成员变量
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
1.3 特殊的声明(匿名结构体类型)
我们在声明结构体的时候,可以不完全的声明
例如
int main()
{
struct
{
int age;
char name[20];
char sex[5];
}s1;
struct
{
int age;
char name[20];
char sex[5];
}*p;
return 0;
}
这两个结构在声明的时候忽略了标签,这样是可以的,但是我们在上面代码的基础上,加上这行代码,会出现什么呢?
p = &s1;
有的小伙伴可能已经知道答案了,对,编译器会报错。
虽然这两个结构的成员完全相同,但是编译器会把它们两个当作完全不同的类型,所以会出现错误,这对于我们来说是不方便的,所以我们在声明结构体的时候一定要加上标签。
1.4 结构的自引用
我们想要在结构中包含一个类型为该结构本身的成员,可以吗?
答案是可以。我们先来看一下错误示范
struct Node
{
int data;
struct Node next;
//这样直接引用是不对的,会导致无限循环
}
正确的自引用:
struct Node
{
int data;//4个字节
struct Node* next;//4或8个字节
//大小确定了,结构体也就确定了
}
1.5 结构体变量的定义和初始化
有了结构体类型,变量如何定义和初始化呢,so easy.
struct student
{
char name[30];
int age;
char sex[5];
char id[20];
}p1; //声明类型的同时定义变量p1
struct student p2; //定义结构体变量p2
//初始化:定义变量的同时赋值
struct student p3 = {"zhangsan",18,"nan",220206401};
struct Node
{
int data;
struct student p;
struct Node* next;
}n1 = {10,{"zhangsan",18,"nan",220206401},NULL }; //结构体嵌套初始化
struct Node n2 = { 20,{"lihua",18,"nan",220206435},NULL };//结构体嵌套初始化
1.6 结构体内存对齐
我们已经了解结构体的基本使用了,那么它的大小该如何计算呢?
我们先学习一下计算的规则,再来几道题目练练手
1.结构体的第一个成员永远都放在0偏移处
2.从第二个成员开始,以后的每一个成员都要对齐到某个对齐数的整数倍处
这个对齐数是:成员自身大小和默认对齐数的较小值
备注:VS的默认对齐数是8,gcc环境下没有默认对齐数,对齐数默认就是成员本身大小
3.当成员全部存放后,结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍,如果不够,则会浪费空间对齐。
4.如果嵌套了结构体,嵌套的结构体成员要对齐到自己成员的最大对齐数的整数倍处。
整个结构体的大小,必须是最大对齐数的整数倍,最大对齐数包含嵌套结构体成员中的对齐数
例题:
struct s1
{
int a; // 4 8 4
char c; // 1 8 1
//和默认对齐数相比,取小,则a取4,c取1
};
struct s1 s;
struct s1
{
char c;
int a; // 4 8 4
};
struct s1 s;
struct s2
{
double a;
char b; // 1 8 1
int c;//4 8 4
};
struct s2 s;
就展示这么多了,大家自己多多练习,熟能生巧。
通过例题,可以得到一个小技巧:创建结构体时将占用空间小的类型凑在一起,可以节省空间。
1.7 修改默认对齐数
VS的默认对齐数是8,好烦呀,怎么办呢?其实我们可以修改结构体的默认对齐数,只需用#pragma这个预处理指令即可。如下:
#include<stdio.h>
#pragma pack(1) //修改默认对齐数为1
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
1.8 结构体传参
直接上代码
#include<stdio.h>
struct S
{
int data[1000];
int size;
};
//传值
void print(struct S s)
{
printf("%d", s.size);
}
//传址
void print(struct S* sp)
{
printf("%d", sp->size);
}
int main()
{
struct S s1;
print1(s1);
print2(&s1);
}
这里有个小问题,是使用print1好还是print2好?
使用print2比较好,因为print1是传值调用,print2是传址调用,传址调用比传值调用更省时间和空间。
结语
学习完结构体后,我们可以通过写一些小程序来练练手,例如通讯录,检验一下自己结构体的学习程度,下一篇内容也是关于通讯录,有问题欢迎大家交流!