共用体
共用体和结构体很相似,只不过关键字由struct变为了union,区别在于:结构体为所有成员变量开独立的内存,而共用体定义了一块能容纳所有数据成员共享的内存。这也就确定了
声明形式如下
union 共用体名
{
成员列表;
};
定义一个结构体
union Data
{
int i;char c;
double d;
}
他和结构体的引用和初始化一模一样,不再赘述。
有必要谈一谈的是共用体类型的数据特点
1,同一段内存可以用来存放几种不同类型的成员,但是每次只能够存放他们其中的一种,而不能同时存放所有的类型,这也代表在共用体中,同事只能有一个成员起作用,其他成员不起作用。
2,共用体初始化后起作用的成员是最后一次存放的成员,再存入一个新的值后,前边的所有成员就失去作用,如果要调用其中的某个成员,那么该成员就起作用,另外的成员不起作用。
3,共用体变量的地址和他各个成员的地址相同。
4,因为共用体的地址和各成员地址一样,不能对共用体变量名赋值,也不可以企图引用变量来得到一个值,违反了程序的确定性。
枚举类型
利用关键字enum可以声明枚举类型,这也是一种数据类型,使用枚举类型可以定义枚举类型变量,几个枚举变量为一组同类型的标识符,每个标识符都对应一个整数值,称为枚举常量。
定义一个枚举类型变量
enum Colors{
RED,
GREEN,
BLUE
};
在括号中,第一个标识符就对应1,第二个对应2,以此类推。
每个标识符都必须是独特嘚!
也可以为某个标识符设置其对应得整形值,后边的标识符依次加一。
例如:
enum Colors{
RED=1,
GREEN,
BLUE
};
此时GREEN就是2,BLUE就是3。
枚举类型通常和switch配合使用,case后边只能是整形数字,使用枚举就解决了这一问题,让代码功能更加清晰。
代码如下
typedef enum Colors { RED = 1, BLUE, GREEN }color; int main() { int icolor; scanf("%d", &icolor); switch (icolor) { case RED: printf("RED\n"); break; case BLUE: printf("BLUE\n"); break; case GREEN: printf("GREEN\n"); break; default: break; } return 0; }
结构体内存对齐
结构体内存对齐是一个十分热门的考题,这里一定要正确记住内存对齐的规则。
结构体的的大小不是里面变量类型的大小累加得到的,而是通过默认的结构体对齐规则,再通过计算得到的。
要记住的是
1,第一个成员在偏移量为0处。
2,第一个后边的成员对齐到变量大小与最小对齐数中小的那个的整数倍处。
对齐数:编译器默认的一个最小对齐数和该成员变量大小的较小值
VS:最小对齐数默认为8
可以用#pragma pack(4)更改默认对齐数
Linux:没有默认对齐数,对齐数就是成员函数本身。
3,结构体总大小为最大对齐数的整数倍
4,如果结构体中嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍(包含嵌套结构体的对齐数)。
为什么存在内存对齐?
1,平台原因
不是所有的硬件都能访问任一地址上的任意数据,某些平台只能在某些地址处取出某些特定类型的数据,不然会报错,为了互容,出现了内存对齐。
2,性能原因
数据的结构应该尽可能在自然边界上对齐,原因在于为了访问没有对齐的内存,处理器需要两次内存访问,而对齐的内存只需要访问一次即可。也就是用空间来换时间。
结构体大小计算
看下边两种结构体
struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { printf("%d ", sizeof(struct S1)); printf("%d ", sizeof(struct S2)); return 0; }
打印结果
一定要记住,内存对齐是从0开始的,所以所占内存是对齐到的位置加1。
第一个结构体:
不信的话我们可以通过offsetof函数来查看结构体成员,不要忘记包含头文件stddef.h
代码如下
#include <stddef.h> struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { printf("%d\n", offsetof(struct S1, c1)); printf("%d\n", offsetof(struct S1, i)); printf("%d\n", offsetof(struct S1, c2)); printf("%d ", sizeof(struct S1)); printf("%d ", sizeof(struct S2)); return 0; }
运行结果如图所示
验证了我们的猜想。
第二个结构体:
可以发现,结构体内部装有相同的数据,只不过排布顺序不一样,就产生了4个字节的浪费,这只不过是两个小小的结构体,才四个字节,然而万一是一个链表呢?成千上万个节点,每个节点浪费4个字节,那就开销很大了,所以我们也要注意结构体的排布问题。
修改默认对齐数求大小
#pragma pack(4) //更改默认对齐数 struct A { char c1; int i; char c2; double d; };
这个结构体的大小是多少呢?
要注意的是,默认对齐数已经被更改了,double类型的数据不会对齐到8的倍数,而是修改后的VS提供的默认对齐数。
在VS里运行一下看一看
没有问题!如果没有修改默认对齐数的话,就会对齐至16的位置,从16往后走8个字节,23-16+1=8(包含16,所以停在23的位置),23-0+1=24(从零开始,故-0+1),刚好是8的倍数,将更改默认对齐数的代码注释再次运行
结果如我们所料。
嵌套结构体计算大小
再建造一个结构体,嵌套后观察大小,上边的结构体大小没有修改默认对齐数的话大小为24。
代码如下
struct A { char c1; int i; char c2; double d; }; struct B { char c; struct A a; int k; }; int main() { printf("%d ", sizeof(struct A)); printf("%d ", sizeof(struct B)); return 0; }
结果是多少呢?
我们来推导一下:
在VS里跑一下验证结果是否正确
结构体的内存对齐规则和大小计算你学费了吗?
联合体的大小计算
union Un { short s[7]; int n; }; int main() { printf("%d ", sizeof(union Un)); return 0; }
联合体只会开辟最大的一个成员的内存,第一个成员的内存为14,int的内存为4,所以选择第一个成员,默认对齐数为8,最后的结果为8的倍数,故最终联合体的大小为16。
运行代码后结果如下
ok.今天的文章就结束啦,欢迎大家一起交流进步!