编译器会为结构体变量中的每个数据成员分配不同的地址空间, 也就是说, 结构体变量中的数据程序是并列关系, 而编译器为共用体变量中的数据成员分配的是同一块内存, 每个时刻只有一个数据成员有意义, 从地址的角度来看两者的差异, 形象地表明了这一点
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- // 初始化方式和结构体类似
- union data{ // 类似于结构体变量, 共享内存, 共用体的大小等于最长的那一段, 共用体变量在任何时刻只有一个成员变量存在, 定义的时候不能初始化, 只有在创建的时候才能初始化
- int price;
- char brand[20];
- }d1, d2, *pData, data[10];
- union key{ // 共用体会存在字节数填充, 直到可以被最小类型字节数整除, double 有 8 个字节, a 数组有 9 个, 再填充 7 个字节, 恰好被 double 整除
- // 内存对齐的方式, 分别 CPU 寻址
- double d;
- char a[9];
- } k; //sizeof(k) =16
- void main(){
- printf("%d\n", sizeof(d1)); //20
- d1.price = 100;
- strcpy(d1.brand,"IBM");
- d2 =d1; // 共用体变量可以直接赋值
- printf("%d,%s\n",d1.price,d1.brand); //5063241,IBM 只正确显示, 最后一个赋值的变量, 其他的数据解析为错误数据, 因为他们共用一段内存
- union data uniondata = {100 }; // 大括号初始化时, 只能初始化第一个成员变量!!! 共用同一片空间
- // 共用体变量的三种引用方式和结构体变量类似
- }
- // 初始化方式和结构体类似
- union data{ // 类似于结构体变量, 共享内存, 共用体的大小等于最长的那一段, 共用体变量在任何时刻只有一个成员变量存在, 定义的时候不能初始化, 只有在创建的时候才能初始化
- int price;
- char brand[20];
- }d1, d2, *pData, data[10];
- union key{ // 共用体会存在字节数填充, 直到可以被最小类型字节数整除, double 有 8 个字节, a 数组有 9 个, 再填充 7 个字节, 恰好被 double 整除
- // 内存对齐的方式, 分别 CPU 寻址
- double d;
- char a[9];
- } k; //sizeof(k) =16
- void main(){
- printf("%d\n", sizeof(d1)); //20
- d1.price = 100;
- strcpy(d1.brand,"IBM");
- d2 =d1; // 共用体变量可以直接赋值
- printf("%d,%s\n",d1.price,d1.brand); //5063241,IBM 只正确显示, 最后一个赋值的变量, 其他的数据解析为错误数据, 因为他们共用一段内存
- union data uniondata = {100 }; // 大括号初始化时, 只能初始化第一个成员变量!!! 共用同一片空间
- // 共用体变量的三种引用方式和结构体变量类似
结构体变量占据的内存单元的个数应当大于等于其内部所有数据成员占据内存单元数的和
出于效率的考虑, C 语言引入了字节对齐机制, 一般来说, 不同的编译器字节对齐机制有所不同, 但还是有以下 3 条通用准则:
(1)结构体变量的大小能够被其最宽基本类型成员的大小所整除;
(2)结构体每个成员相对于结构体首地址的偏移量 (offset) 都是成员大小的整数倍, 如有需要编译器会在成员之间加上填充字节(internal adding);
(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍, 如有需要编译器会在最末一个成员之后加上填充字节(trailing padding).
字节对齐第 3 条准则提及最宽基本类型的概念, 所谓基本类型是指像 char,short,int,float,double 这样的内置数据类型."数据宽度" 就是指其 sizeof 的大小. 诸如结构体, 共用体和数组等都不是基本数据类型
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- // 第一步: 找出最宽的基本数据类型
- // 第二步: 每个成员变量都可以被最宽基本类型整除
- // 第三步: 累加
- struct data1{
- char c; // 4 char 必须能被最宽基本类型整除, 所以也是 4 个字节
- int price; // 4 int 是最宽基本数据类型 4 个字节
- char brand[19];// 20 数组是不是基本数据类型, 基本数据是 char 类型, 也要能被 4 整除 所以填充 1 个字节, 就是 20
- }d1; //sizeof(d1)=28
- struct data2{
- char c; // 1 char 必须能被最宽基本类型整除, 所以也是 1 个字节
- char price; // 1 char 是最宽基本数据类型 1 个字节
- char brand[19];// 19 数组是不是基本数据类型, 基本数据是 char 类型, 也要能被 1 整除 无需填充
- }d2; //sizeof(d2)=21
- struct data3{
- char c; // 8 char 必须能被最宽基本类型 8 整除, 所以也是 8 个字节
- double price; // 8 char 是最宽基本数据类型 8 个字节
- char brand[19];// 24 数组是不是基本数据类型, 基本数据是 char 类型, 也要能被 8 整除, 填充 5 个字节, 24 个字节
- }d3; //sizeof(d3)=40
- void main(){
- printf("%d\n", sizeof(d3));
- printf("%x\n",&d1);
- printf("%x\n",&d1.c);
- printf("%x\n",&d1.price);
- printf("%x\n",&d1.brand);
- /*
- * 406400 &d1
- * 406400 &d1.c +4
- 406404 &d1.price + 4
- 406408 &d1.brand +20
- */
- }
来源: http://www.bubuko.com/infodetail-3153087.html