2021-09-05_user_defined_data_types(自定义数据类型)

文章目录

1,结构体

结构体类型的声明

    结构体的基础知识:  结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量

接下来,让我们通过程序来进一步加深我们对结构体的认识

程序一:

#include<stdio.h>
//声明一个结构体类型
//声明一个学生类型,是 想通过 学生类型 来创建  学生变量(对象)
// 描述学生 : 属性 + 名字 + 性别 + 年龄 + 电话 
struct student// struct  结构体 关键字  student 结构体 标签
{
	char name[10];
	char sex[20];
	int age;
	char telephone[12];// 这四个变量 就是结构体的成员变量
}s4,s5,s6; // 创建 结构体 全局变量
// 以上所有 就是一个 结构体类型

struct student s3; // 创建 结构体 全局变量

int main()
{
	//创建 结构体变量 的方式
	struct student s1;
	struct student s2;  // 因为 这两个变量放在 main 函数里,所以是 局部变量


	return 0;
}

特殊的声明

程序一:

#include<stdio.h>
struct          // 缺少一个标签(名字) 这种结构体类型 称为 匿名结构体类型     
{             // 那么问题来了,没有名字 该 如何 创建 结构体 变量 ?
	char name[10];
	char sex[20];
	int age;
	char telephone[12];
}x;// 只有一种方式在结构体末尾分号(;)前面   创建 结构体 全局变量


struct          // 缺少一个标签(名字) 这种结构体类型 称为 匿名结构体类型     
{             
	char name[10];
	char sex[20];
	int age;
	char telephone[12];
}* px;//  在 匿名结构体 的 全部变量 px 前面加上 * ,该 匿名结构体类型 变成了 匿名结构体指针类型
// 即 * px  是一个结构体指针


int main()
{
	px = &x; // 经过 编译器 编译, 程序报警,该表达式 是 不合法的
	//因为 编译器, 会把 它们 当做 2 种 不同类型 来处理
	// 所以 两种 不同类型 的数据,是无法进行赋值
	return 0;
}

结构体自引用

程序一:

#include<stdio.h>
struct node
{
	int data;
	struct node* next;// 如果 没有 * 号,也就是数 next 是本身的结构体变量,会导致这个结构体所占内存无限大。
    // 这里存地址, 存的是下一个数据的地址:
    //结构体自引用 就是 结构体 用指针 找到 与自身同类型的  结构体变量
	// 而不是说 结构体自己 包含 结构体自己 的 变量  : struct node next  (error)
};
int main()
{

	return 0;
}


程序二:


#include<stdio.h>
typedef struct node  // 这里的 node 是不能省略的, 要不然 下面 没有这 struct node* next  类型,只能写成 node* next
{                    // 但是 node 是 [把 省略了 node 从而 变成 匿名结构体 的 重命名]。 是后有的,
	                 //  也就是说 node 还没有生成, 就在结构体 调用它,
	                 // 这种写法 是错误的
	int data;//数据域
	struct node* next;// 指针域
}node;   // node : typedef 把 结构体 struct node  简化成 node
int main()
{
	node n;
	return 0;
}


结构体变量的定义和初始化

程序一:

#include<stdio.h>
struct s
{
	char c;
	int a;
	double d;
	char arr[20];
};

int main()
{
	struct s s = { 'c', 100, 3.14, "hellworld" };
	printf("%c %d %lf %s\n", s.c, s.a, s.d, s.arr);
	return 0;
}


程序二:

#include<stdio.h>
struct t
{
	double weight;
	short age;

};


struct s
{
	char c;
	int a;
	double d;
	char arr[20];
	struct t st;
};


int main()
{
	struct s s = { 'c', 100, 3.14, "hellworld", {55.6,30} };
	printf("%c %d %lf %s %lf %d\n", s.c, s.a, s.d, s.arr, s.st.weight, s.st.age);
	return 0;
}

结构体内存对齐: 用来计算结构体 的 内存大小

结构体的对齐规则:
  1. 第一个成员 在与 结构体变量 偏移量为 0 的 地址处(第一个 结构体成员 存储的地址,即结构体第一个结构体变量 存储地址,地址为 0)。
  2. 其他成员 变量 要对齐某个数字(对齐数)的 整数倍 的 地址处

对齐数 = 编译器默认 的 一个对齐数 与 成员大小的 较小值
vs 中 默认的值为 8; gcc 没有默认对齐数(成员的大小,就是对齐数)

比如 结构体里 有一个 成员(变量) 为 整形 int 类型 为 4byte
// 而 vs 中 默认值为 8, 4 < 8, 取 4,
// 那么 该成员的 对齐数 为 4

  1. 结构体总大小为 最大 对齐数(每个成员 的 变量 都有 一个对齐数)的整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体 对齐 到自己的 最大 对齐数 的整数倍处,
    // 结构体 的 整体大小 就是 所有 最大对齐数(含嵌套结构体的对齐数)的整数倍。
程序一:
struct s
{
	char c1;//  第一个成员 在与 结构体变量 偏移量为 0 的 地址处 (内存所占 1 字节)
	int a; //  a 对齐数 是 4 ,因为 其他成员 变量 要对齐 对齐数(4) 的 整数倍 的 地址处(地址4)
	// 从 c1(0 地址) 后面开始(从地址 4 开始) 地址 4 处 存放 a,就是说 c1  与 a 之间 隔了 3 个 地址(1,2,3) -> 3 字节
	// 此时 a 末尾地址 为 地址8 (因为 a 的存储 需要 4 byte 空间) 
	char c2;//  c2 对齐数 1 ; 其他成员 变量 要对齐 对齐数(1)的 整数倍(倍数为 1) 的 地址处  也就是紧跟 a 后面的 地址8,(char 1 byte)存完之后,末尾地址指向 9;
	                                                                                            // 无论是地址几,都是 对齐数 1 的倍数
	// 至此,1+3+4+1 为 9 字节
	// 结构体 总大小 为9 字节,但此时地址,不是 成员中 最大 对齐数(4) 的整数倍,
	//  12 满足
	// 所以 结构体的大小 最后 为 12 byte
};

struct s2
{
	char c1;//  第一个成员 在与 结构体变量 偏移量为 0 的 地址处 (1字节)
	char c2;// 对齐数为 1 -> 其他成员 变量 要对齐 对齐数的 整数倍( 倍数为 1 ) 的 地址处(地址1) ,c2 的存储地址 紧跟在 c1 的后面(2 字节)
	int a;// 对齐数 4   -> 其他成员 变量 要对齐 对齐数的 整数倍( 倍数为 4 ) 的 地址处(地址4),(c1 是 0 地址,c2 是1 地址,浪费 2,3地址)
	//   即 来到 地址4, 也就是 a 的地址,也就是说 a 与 c2  隔了 2 个地址(浪费了2字节空间),a 的 存储  也要 占 4 字节
	//  1 + 1 + 2 + 4 == 8 字节 
	//   成员中 最大对齐数(4) 的整数倍
	//  因为 8 == 2*4  > 6 满足条件
	// 所以最后 结构体总大小 为 8

};

int main()
{
	struct s s = { 0 };
	struct s2 s2 = { 0 };
	printf("%d\n", sizeof(s));// 12
	printf("%d\n", sizeof(s2));// 8
	return 0;
}
struct s1附图 :

在这里插入图片描述
12 byte ,满足成员中 最大 对齐数(4) 的整数倍, 即 结构体大小 为 12 byte

struct 附图2:

在这里插入图片描述

程序二:
#include<stdio.h>
struct s3
{
	double d;// 第一个成员 在 与 结构体 偏移量为 0 的地址处(double 8字节,此时地址 指向地址7)
	char c;// 对齐数 1 地址8(9字节)
	int i;// 对齐数 4   c 后面的 是 地址9,不满足倍数条件,地址 12 满足(浪费 9,10,11地址,即 3字节空间),即 i 的地址 是 地址12
	// i 占 4 字节,
	// 8 + 1 + 3 +4 == 16
	//  16 满足成员中 最大对齐数(8)的整数倍
};
int main()
{
	printf("%d\n", sizeof(struct s3));// 16
}

附图:
在这里插入图片描述

程序二:
#include<stdio.h>
struct s3
{
	double d;// 对齐数 8  地址 7
	char c;// 对齐数 1  地址 8
	int i;// 对齐数 4  地址 12    i 存储 需要 4byte ,地址 16
	//  结构体大小 16 byte 满足 最大 对齐数(8) 的整数倍
	// 故结构体 真正大小 为 16字节
};
struct s4
{
	char c1; // 地址0, 对齐数 1

	// 嵌套了结构体的情况,嵌套的结构体 对齐 到自己的 最大 对齐数(8) 的整数倍处
	struct s3 s3; // 对齐地址 8   然后 结构体 s3 内存大小 为 16 byte
	             // 存完16byte之后,地址 24
	double d; // 对齐数 8 ,地址24  满足 对齐数 整数倍 地址 
	           // 地址 24 到 地址 32 
	           //  32 满足 最大 对齐数(8)的整数倍
	           //  结构体 变量 s4  内存大小 32 字节 

};
int main()
{
	printf("%d\n", sizeof(struct s4));// 32
}

附图:
在这里插入图片描述


为什么存在内存对齐 : 空间(浪费的) 换取 时间

1. 平台原因(移植原因):不是所有的硬件平台都能访问地址上的任意数据的,

某些硬件平台智能在某些地址处取某些特定类型的数据。否则抛出硬件异常。

2.性能原因:数据结构(尤其是栈)应尽可能地在自然边界sang对齐,原因在于,为了访问 未对齐 的内存,

处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
那么在 设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?

struct s
{
	char c1;//  第一个成员 在与 结构体变量 偏移量为 0 的 地址处 (内存所占 1 字节)
	int a; //  a 对齐数 是 4 ,因为 其他成员 变量 要对齐 对齐数(4) 的 整数倍 的 地址处(地址4)
	// 从 c1(0 地址) 后面开始(从地址 4 开始) 地址 4 处 存放 a,就是说 c1  与 a 之间 隔了 3 个 地址(1,2,3) -> 3 字节
	// 此时 a 末尾地址 为 地址8 (因为 a 的存储 需要 4 byte 空间) 
	char c2;//  c2 对齐数 1 ; 其他成员 变量 要对齐 对齐数(1)的 整数倍(倍数为 1) 的 地址处  也就是紧跟 a 后面的 地址8,(char 1 byte)存完之后,末尾地址指向 9;
	                                                                                            // 无论是地址几,都是 对齐数 1 的倍数
	// 至此,1+3+4+1 为 9 字节
	// 结构体 总大小 为9 字节,但此时地址,不是 成员中 最大 对齐数(4) 的整数倍,
	//  12 满足
	// 所以 结构体的大小 最后 为 12 byte
};



struct s2
{
	char c1;//  第一个成员 在与 结构体变量 偏移量为 0 的 地址处 (1字节)
	char c2;// 对齐数为 1 -> 其他成员 变量 要对齐 对齐数的 整数倍( 倍数为 1 ) 的 地址处(地址1) ,c2 的存储地址 紧跟在 c1 的后面(2 字节)
	int a;// 对齐数 4   -> 其他成员 变量 要对齐 对齐数的 整数倍( 倍数为 4 ) 的 地址处(地址4),(c1 是 0 地址,c2 是1 地址,浪费 2,3地址)
	//   即 来到 地址4, 也就是 a 的地址,也就是说 a 与 c2  隔了 2 个地址(浪费了2字节空间),a 的 存储  也要 占 4 字节
	//  1 + 1 + 2 + 4 == 8 字节 
	//   成员中 最大对齐数(4) 的整数倍
	//  因为 8 == 2*4  > 6 满足条件
	// 所以最后 结构体总大小 为 8

};



根据 剖析, 发现 结构体成员相同,但是 让 占用空间小的的成员尽量集中在一起 ,节省很多的空间(也就是不至于浪费很多空间)

修改默认对齐数

之前我们见过了 #pragma 这个预处理指令,这里让我们再次使用,可以改变我们的默认对齐数

程序一:

#include<stdio.h>
#pragma pack(4)   // 设计 默认对齐数 为 4
struct s
{
	char c1;// 1
	
	// 浪费 3 个字节(原本要浪费 7 个 字节,现在只需 3 个字节)
	double d; // 8 byte 对齐 为 对齐数 4,【4与8 选择较小的】 的整数倍 的地址
	// 存储 d  需要 8 个字节 加上前面浪费 3 个 和 c1 1个 字节
	// 1+3+8 == 12 字节  
	// 这样写法,帮我们 避免 了 4 字节 的 空间浪费
};
#pragma pack() // 取消设置的 默认 对齐数


程序二:

 那 我们把 默认对齐数 设置为 1 呢?
#include<stdio.h>
#pragma pack(1)   // 设计 默认对齐数 为 1
struct s
{
	char c1;// 1
	// 一个字节的都不会浪费
	double d; // 8 byte 对齐 为(对齐数 1,【1与8 选择较小的】) 的整数倍 的地址
	// 存储 d  需要 8 个字节 
	// 1+8 == 9 字节  (9 是 最大 对齐数(1)的 整数倍)
	// 这样写法,帮我们 避免 了 7 字节 的 空间浪费
};
#pragma pack() // 取消设置的 默认 对齐数

// #pragma pack() 一般设置 默认 对齐数 为 2,4,8, 16   (2的次方数)

百度面试题

写一个宏,计算结构体中某变量 相对于首地址的偏移,并给出说明 (考察 offsetof 宏的实现)

#include<stdio.h>
#include<stddef.h>
struct s
{
	char c;
	int i;
	double d;
};

int main()
{
	// offsetof 其实是一个宏,用来表示   成员 相对于 结构体 的 偏移量
	printf("%d\n", offsetof(struct s, c));// 0
	printf("%d\n", offsetof(struct s, i));// 4
	printf("%d\n", offsetof(struct s, d));// 8
	return 0;      //而且 offsetof 的 参数 传的是 一个类型,更加说了 offsetof 是一个宏 
}


结构体传参

#include<stdio.h>
struct s
{
	int a;
	char c;
	double d;
};

void init(struct s *tmp)
{
	tmp->a = 100;
	tmp->c = 'w';
	tmp->d = 3.14;
}

void print(struct s tmp)// 传值:如果传递的结构体对象的时候,结构体过大(空间过大),容易导致 参数压栈 的 系统 开销比较大,从而导致系统性能下降。
{
	printf("%d %c %lf\n", tmp.a, tmp.c, tmp.d);
}

void print2(const struct s* tmp) // 传址 : 最好使用这种方法(节省空间),一个地址在操作系统不改变的情况,永远都是4个字节大小,
{       // const 是为了防止 意外改变 结构体变量地址 指向的 值
	printf("%d %c %lf\n", tmp->a, tmp->c, tmp->d);
}

int main() 
{
	struct s s = { 0 };
	init(&s);
	print(s);
	print2(&s);
	return 0;
}


结构体 实现 位段 ( 位段的填充 & 可移植性 )

位段 的 声明 和 结构 是 类似的,有 两个 不同:

1、 位段 的 成员 必须是 int,unsigned int 或 signed int(还可以是 char )
2、 位段 的 成员 后边 有 一个冒号 和一个数字( 数字 <= 32 [有多少位操作系统决定] )

位段 - 位:二进制位

位段的内存分配:
1、位段的成员可以是 int unsigned int,signed int 或者是char( 属于整形家族 )类型
2、位段的空间上是按照需要 以 4个字节(int)或者1个字节(char)的仿古式来开辟
3、位段涉及很多不确定因素,位段是不跨平台的,注重 可移植 的 程序 应该 避免使用 位段


程序一:

#include<stdio.h>
struct a  // 按照以下写法写结构体成员, a 已经不是个结构体类型了,而是 一个 位段 类型了
// 位段 看见成员 都是 int 类型 ,所以,它 一开始 就创建了 4 byte的 空间
{
	int a : 2;// 2 这里的意思是: a只需要 2 个比特位(bit)
	int b : 5;// 5 这里的意思是: b只需要 5 个比特位(bit)
	int c : 10;// 10 这里的意思是:  c只需要 10 个比特位(bit)
	int d : 30;// 30 这里的意思是: d只需要 30 个比特位(bit)
};// 一共 47 个 bit 位,由已经创建的 4byte 空间来分配空间, 很明显 空间不够 大,只能存入 a,b,c,4 byte 空间 还剩 15 bit
   d 放不下,怎么办呢?  在vs 环境中 系统 会舍弃(浪费)剩余的 15 bit 空间
 然后, 它再向 空间申请 4 byte(32bit) 空间, 存储 d 需要 30 bt 空间,这 30 bit 的数据 就存入 这个向系统第二次申请 4 byte 的空间里
 至此,数据全部存完,剩余的空间就浪费掉了,也就是说 这 位段 的 内存大小为 8 byte
int main()
{
	struct a a;
	printf("%d\n", sizeof(a));// 8 byte
	return 0;
}

附图:
在这里插入图片描述
&ensp;

位段的意义:

经过上程序,我们发现 原本 a 需要占 4 byte空间,但是 a 只需要 2 bit 的空间, 这样就形成巨大的空间浪费。 所以 我们通过 位段 这种方式 大大减小了 空间上浪费的问题
(虽然位段也有浪费,但是相比 结构体 浪费的空间要少, b c d 也是同理)

程序一:

#include<stdio.h>
// vs 环境
struct s // 因为下面 成员类型 为 char 类型,所以它 一开始 就准备 了 1 byte 空间
{
	char a : 3;//  a 要个 3 bit 位, 由提前准备 1 byte 空间 来分配,还剩 5 bit
	// 

	char b : 4;// b 要 4 bit 位,由提前准备 1 byte 空间 来分配,还剩 1 bit 空间
	char c : 5;// c 要 5 bit 位,由提前准备 1 byte 空间 来分配(剩余 1 bit),不够大,
	// 舍弃(浪费)掉,再向内存申请 1 byte 的空间 来 存储 c (剩余 3 bit 空间)  

	char d : 4;// d 需要 4 bit 位,剩余内存空间(3 bit)不够,把 3 bit 浪费掉(舍弃)
	// 再向内存申请 1 byte 空间,来存储 d,(剩余 4 bit 空间)
	// 至此 数据全部 存储完毕,剩余的空间 舍弃掉(浪费了)
	//  一共向内存 申请了  3 byte 的空间
};

int main()
{
	struct s s = { 0 };
	s.a = 10;// 1010  因为 a 只有 3 bit 位  所以存入的是 010
	s.b = 20;// 10100 因为 b 只有 4 bit 位  所以存入的是 0100
	s.c = 3;//  0011  因为 c 只有 5 bit 位  所以存入的是 00011
	s.d = 4;//  0100  因为 d 只有 4 bit 位  所以存入的是 0100
	return 0;
}

附图:
在这里插入图片描述

化作 16 进制 0x 22 03 04 (可以通过调试 -> 内存 -> &s 来观察 )

位段 的 跨平台问题

1、 int 位段 被当成 有符号数 还是 无符号数 是不确定的。
2、 位段中 最大位的数目 不能确定。(16 位 机器最大 16,32位机器最大32。写成 27,在 16 位 机器会出现问题)
3、 位段中 的 成员 在内存中 从左向右分配,还是从右向左 分配标准 尚未定义(由编译器决定)。
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳与 第一个 位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的(由编译器决定)。

总结:

位段 跟 结构 相比,位段可以达到同样的效果,且可以很好的节省空间,但是 有 跨平台的问题存在。 位段 不能 跨平台 使用


2,枚举: 顾名思义就是 -> 列举(把可能的取值 一 一 列举)

举几个现实生活的例子
1、一周 的 星期一 到 星期日 是 有限的 7天,可以 一 一 列举
2、性别有:男、女、保密,也可以 一 一 列举
3、月份有 12 个月,也可以 一 一 列举

枚举类型的定义

程序一:

enum sex// 性别
{
	// 枚举的可能取值(枚举常量)
	male,// 0
	female,// 1
	secret// 2
};

enum color // 颜色
{
	// 枚举的可能取值(枚举常量)
	red,// 0
	green,// 1
	blue// 2
};


int main()
{
	enum day d = mon;//枚举赋的值,只能是 枚举的 可能取值
	enum sex s = male;//枚举赋的值,只能是 枚举的 可能取值

	enum color c = blue;//枚举赋的值,只能是 枚举的 可能取值
  blue 在枚举的里面 是 2, 那我们 可不可以这样写 enum color c = 2  ?
  答案是不行,右边 2int 类型, 左边的 c 是 enum color 类型

	printf("%d %d %d\n", male, female, secret);// 0 1 2
	printf("%d %d %d\n", red, green, blue);// 0 1 2
  printf("%d\n",sizeof(s)); // 枚举大小为 4 byte ,为什么呢?
                               因为 枚举常量 的类型是 整形,
                              那 s 就是整形变量,所以输出为 4
	return 0;
}



程序二:

#include<stdio.h>
//#define red 0  // 这里的 red 是一个符号, 程序 遇到 red 替换成 0
//#define green 1
//#define blue 2


enum color
{
	// 枚举的可能取值(枚举常量)
	red = 1,
	green = 3,
	blue = 5
};



int main()
{
    //int color = red; 


                                           // 枚举
	printf("%d %d %d\n", red, green, blue);//[red = 2 ]       2 3 4
	                                      //[green = 2 ]      0 2 3 
	                                      // [ green = 2 ,  blue = 9]    0 2 9
	                                      // [ red = 1、 greem = 3、blue = 5]    1 3 5
	return 0;
}


枚举的优点

        为什么要是使用枚举
        1. 增加代码 的 可读性 和 可维护性
        2. 和 #define 定义 的 标识符 比较 枚举 有类型检查,更加严谨。
        3. 防止了 命名污染(命名冲突,互相干扰)
        4. 便于调试
        5. 使用方便,一次可以定义多个常量

小知识点

C语言的源代码 – 预处理(预编译)-> 编译 -> 链接 -> 可执行程序
在预编译中 就完成了 define 的替换

3. 联合 == 联合体(共用体)

联合类型的定义

    联合也是一种特殊的自定义类型,这种类型 定义的变量 也包含一系列的成员,特征是这些成员公用同一块空间
              // 所以 联合 也叫 联合体 / 共用体


程序一:

#include<stdio.h>
union un // 创建联合体
{
	char c;// 对齐数 1
	int i; // 对齐数 4

};
int main()
{
	union un u;// 联合体变量 u
	printf("%d\n", sizeof(u));// 4  
	printf("%p\n", &u);// 00D7FBBC
	printf("%p\n", &(u.c));// 00D7FBBC
	printf("%p\n", &(u.i));// 00D7FBBC
	// 你会发现 联合体(共用体)变量 和 成员 都是用的同一块空间
	// 这意味着 一次 只能用一个成员,要不然会出问题
	return 0;
}

为什么 该联合体 的大小为 4 呢?跟着我往下看,你就会知道为什么为 4 了。

联合的特点:

           联合的成员 是 共用 同一块 内存空间的,这样 一个 联合变量 的大小,至少是最大成员的大小
           因为 联合 至少得有 能力 保存 最大 的 那个成员

面试题:判断当前计算机大小端存储

#include<stdio.h>
 正常方法
int check_system()
{
	int a = 1;
	return *(char*)&a;// 返回1,表示小端
	                  // 返回0, 表示大端
}

 联合体 方法
int check_system()
{
	union   //  因为 我们只用 一次,所以可以写成 匿名 形式
	{
		char c;  // |-- i --|
		int i;   // 口 口口口
		         // c
	}u;      // |-- i --|
	u.i = 1;// 01 00 00 00  小端字节序存储模式
	        // c
	   // 我们想从 4 byte 里面取出 第一个字节的内容,
	// i 占了 4byte,那么 c  不就是它第一个字节了
	  //  因为 联合 成员是共用 一块空间的,int 访问 4 byte 的内容。char 访问一个字节内容
	// 所以直接 返回 u.c 等于就是把 第一个字节的内容(01) 传回去了
	return u.c;// 返回1,表示小端
//	           // 返回0, 表示大端
}

int main()
{
	int a = 1;
	//低地址                 高地址
	// 01(低)  00      00     00(高)  小端字节序存储模式
	// 00(高)  00      00     01低)  大端字节序存储模式
	int ret = check_system(); 
	if (1 == ret)
	{
		printf("小端存储模式\n");
	}
	else
	{
		printf("大端存储模式\n");
	}
}


联合大小的计算

联合的大小至少是最大成员的大小
当最大成员大小不是最大对齐的整数倍的时候,就要对齐到最大对齐数的 整数倍


程序一:

#include<stdio.h>
union un
{
	int a; // 自身大小是4  默认对齐数 8  取较小值 4,所以对齐数为 4
	char arr[5];//  拿元素的类型,来算对齐数,元素的类型是 char ,大小 为 1
	           //  默认对齐数 为 8 ,取较小值 1,即 对齐数为 1
	//最大 对齐数 是 4,如果我们按照 5 个 byte 就够了(因为数组有 5 个元素)
	// 但是 5 不是 最大对齐数 4 的整数倍
	// 所以浪费 3 字节,5 + 3 == 8 byte
};

int main()
{
	union un u;
	printf("%d\n",sizeof(u));// 8
	return 0;
}


最后总结一下:

联合 和 结构体 存在 对齐

位段 和 枚举 是不存在 对齐的

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dark And Grey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值