1、结构体
1.1、概述
在C语言中,除了常见的基本数据类型(整数类型short、int、long和浮点类型float、double)外,还有派生类型,如指针类型、数组类型、结构类型、共用体类型等。
结构体是基本数据类型不能满足需求时,用户自己指定的一种数据结构,由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体。
声明一个结构休类型的一般形式如下:
struct 结构体名
{成员列表};
结构体名,用作结构体类型的标志,它又称 结构体标记,大括号内是该结构体中的各个成员,由它们组成一个结构体,对各成员都应进行类型声明如:
类型名 成员名;
也可以成员列表称为域表,第一个成员也称为结构体中的一个域。成员名定名规则与定义变量名规则相同。
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
1.2、定义结构体类型变量的方法
前面只是指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元,为了能在程序中使用结构类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据,可以采取以下3种方法定义结构体类型变量。
(1)先声明结构体类型再定义变量名
如上面已定义了一个结构体类型 struct student,可以用它来定义变量。如:
struct student{ //结构体类型名
...
...
...
}student1, student2 //结构体变量名
定义了 student1, student2 为 struct student 类型的变量。
(2)在声明类型的同时定义变量
例如:
struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1, student2;
它的作用与第一种方法相同,即定义了两个 struct student 类型的变量 student1, student2 这种形式的定义的一般形式为
struct 结构体名
{
成员表列
}变量名表列;
(3)直接定义结构类型变量
其一般形式为:
struct
{
成员表列
}变量名表列;
即不出现结构体名。
关于结构体类型,有几点要说明:
- 类型与变量是不同的概念,不是混同,只能对变量赋值,存取或运算,而不能对一个类型赋值,存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。
- 对结构体中的成员(即 域)可以单元使用,它的作用与地位相当于普通变量。
- 成员也可以是一个结构体变量。如:
struct date // 声明一个结构体类型
{
int month;
int day;
int year;
}
struct student
{
int num;
char name[20];
char sex;
int age;
struct date birthday;
char addr[30];
}student1, student2;
先声明一个 struct date 类型,它代表 日期 包括3个成员 month, day, year。然后在声明 struct student 类型时,将成员 birthday 指定为 struct date 类型。
- 成员名可以与程序中的变量名相同,二者不代表同一对象。
1.3、结构体变量的引用
1.4、结构体变量的初始化
1.5、结构体数组
1.6、指向结构体类型数据的指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址,可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
2、结构体内存对齐
2.1、简述
有没有想过一个问题,某些时候我想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢?
此时,#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack()应运而生。
#pragma pack(push)
#pragma pack(n) //自定义按n字节对齐
……
……//结构体
……
#pragma pack() //取消自定义的对齐方式,恢复默认对齐。
或者使用
#pragma pack(pop) //pop会让编译器回到push之前的对齐方式
注意,#pragma pack() 取消自定义对齐方式,恢复默认方式,而push之后pop是回到push指令之前的对齐方式。
2.5、#pragma pack()语法
语法:
#pragma pack( [show] | [push | pop] [, identifier], n )
说明:
1,pack提供数据声明级别的控制,对定义不起作用;
2,调用pack时不指定参数,n将被设成默认值;
3,一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;
语法具体分析:
1,show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;
2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;
3,pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;
4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;
5,n:可选参数;指定packing的数值,以字节为单位;
注意:
#pragma pack(push)
#pragma pack(4)
//上面两句代码等价于下面这句代码:
#pragma pack(push,4)
3、结构体经典面试题
(1)、什么是结构体?
(2)、一般在什么情况下用到结构体?
(3)、什么是结构体内存对齐?为什么要对齐?怎样对齐?
(4)、对齐参数如何设置?可以设置为按照任意字节数对齐吗?
(5)、如何知道结构体某个成员相对于结构体起始位置的偏移量?
参考资料:
http://www.runoob.com/w3cnote/c-structures-intro.html
https://blog.csdn.net/dai_wen/article/details/78304568
https://www.cnblogs.com/yangguang-it/p/7392726.html