一、struct和union的区别
- struct:
struct A{
int a;
long *b;
char c[20];
};
可以看到struct是由各种基本数据类型组合成的一个结构,我们也可以理解为另外一个数据类型,只不过这个数据类型不是C语言语法里面规定的像char、int这样的基本单位。既然是数据类型就具有内存,下面看一段代码:
#includestruct A { }; struct B { int a; int b; int c; }; struct C { char a; int b; int c; }; void test_struct1() { struct A m_a; struct B m_b; struct C m_c; printf("sizeof(struct A) = %dn", sizeof(m_a)); printf("sizeof(struct B) = %dn", sizeof(m_b)); printf("sizeof(struct C) = %dn", sizeof(m_c)); } int main() { test_struct1(); return 0; }
运行输出结果:
可以看到一个空的结构体的内存大小为0,struct B的数据类型的内存大小为int+int+int等于12,是所有成员变量的和,但是为什么struct C的内存大小不是char+int+int等于9呢?因为有字节对齐,在第二部分会讲到字节对齐就明白了。
结构体的嵌套使用
结构体的自引用,就是在结构体内部,包含指向自身类型结构体的指针。
结构体的相互引用,就是说在多个结构体中,都包含指向其他结构体的指针。
(1)自引用结构体
不使用typedef时
错误的方式:
struct D {
int a;
struct D d;
};
看看编译结果:
编译器直接报错了,这种声明是错误的,因为这种声明实际上是一个无限循环,成员D是一个结构体,D的内部还会有成员是结构体,依次下去,无线循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。
正确用法是使用指针:
struct D {
int a;
struct D* d;
};
此时编译通过:
由于指针的长度是确定的(在32位机器上指针长度为4),所以编译器能够确定该结构体的长度。
如果使用typedef该如何定义呢?
先看一下一个简单的例子:
typedef struct E {
int a;
}T_E;
这个编译器是可以编译通过的,我们再看看:
typedef struct E {
int a;
T_E* e;
}T_E;
编译的结果:
编译器直接提示未定义T_E,这是因为使用typedef为结构体创建一个别名T_E。但是这里是错误的,因为类型名的作用域是从语句的结尾开始,而在结构体内部是不能使用的,因为还没定义。
正确的方式:
typedef struct _t_e{
int a;
struct _t_e* e;
}T_E;
此时编译通过:
(2)相互引用 结构体
错误的方式:
typedef struct _t_f{
int a;
G *gp;
} F;
typedef struct _t_g{
int a;
F *ep;
} G;
编译报错:
错误的原因和上面一样,这里类型B在定义之前 就被使用。
正确的方式:
typedef struct _t_e{
int a;
struct _t_e* e;
}T_E;
struct _t_g{
struct _t_f *fp;
int a;
};
struct _t_f{
struct _t_g *gp;
int a;
};
typedef struct _t_g G;
typedef struct _t_f F;
- union
typedef union
{
char A;
int B;
short C;
}UN;
可以看到union也是由各种基本数据类型组合成的一个结构,但是它和结构体有什么区别呢?
结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
先来看一段代码:
union AA {
};
union BB {
int a;
int b;
int c;
};
union CC {
char a;
int b;
int c;
};
printf("sizeof(struct AA) = %dn", sizeof(m_aa));
printf("sizeof(struct BB) = %dn", sizeof(m_bb));
printf("sizeof(struct CC) = %dn", sizeof(m_cc))
我们看一下 AA BB CC 分别占用多少内存:
可以看到没有成员的union和struct一样不占用内存,拥有成员int int int和拥有char int int的都占用4个字节的内存,也就是说union所有成员占用同一段内存,以最大的成员所占有的内存所分配内存。我们再来看看一段代码:
union AA m_aa;
union BB m_bb;
union CC m_cc;
m_bb.a = 10;
printf("m_bb.a = %d, m_bb.b = %d, m_bb.c = %dn", m_bb.a, m_bb.b, m_bb.c);
运行结果:
可以看到我们在对union的成员a赋值的时候,b,c也拥有同样的值,也就是说明a,b,c拥有共同的内存。
我们再看一段代码:
union CC m_cc;
m_cc.b = 20;
printf("m_cc.a = %d, m_cc.b = %d, m_cc.c = %dn", m_cc.a, m_cc.b, m_cc.c);
运行结果:
可以看到union CC中的所有成员也被赋值成同一个值。
二、字节对齐
字节对齐有3条规则:
规则1:结构体的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员放在offset为该数据成员大小的整数倍的地方。如:int在32位机为4字节,则要从4的整数倍地址开始存储。
规则2:如果一个结构体B里嵌套另一个结构体A,则结构体A应从offset为A内部最大成员的整数倍的地方开始存储。(struct B里存有struct A,A里有char、int、double等成员,那A应该从8的整数倍开始存储。)结构体A中的成员的对齐规则仍满足原则1、原则2。
规则3:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足要补齐。
我们来看一个例子:
struct C {
char a;
int b;
short c;
};
先来看看struct C占用的内存大小:
12个字节,这个就和字节对齐有关系:(在32位系统环境,编译选项为4字节对齐)
struct中a占1个字节,b占4个字节,c占2个字节,但因为了满足(结构体每个成员相对于结构体首地址的偏移量offset都是成员大小的整数倍),a需要占4个字节,这样b的offset就是4了,同时为了满足(结构体总大小是内部最大成员整数倍),c需要4个字节。所以一共12个字节。我们来看各自的内存占用情况:
三、用union区分大小端
到这了我们来想一个问题:如何用union区分大小端,首先我们先来了解一下什么是大小端:
比如一个short类型的变量:对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,这称为大端(big-endian)字节序。
假如现有一32位int型数0x12345678,那么其MSB(Most Significant Byte,最高有效字节)为0x12,其LSB (Least Significant Byte,最低有效字节)为0x78,在CPU内存中有两种存放方式:(假设从地址0x4000开始存放)
了解完大小端,我们来用union判断一下我们的服务器是大端还是小端的:
来看一段代码:
union CC {
char a;
int b;
int c;
};
union CC m_cc;
m_cc.b = 10;
if(m_cc.a)
printf("little-endiann");
else
printf("big-endiann");
这里定义了一个union的CC,对成员int b进行赋值为10,因为union是公用内存的,如果是小端模式,10应该存放在地址起始的位置上,那么a成员的值也是10,否则a应该为0,以此来分辨大小端。
四、代码分析:
#includestruct A { }; struct B { int a; int b; int c; }; struct C { char a; int b; short c; }; struct D { int a; struct D* d; }; typedef struct _t_e{ int a; struct _t_e* e; }T_E; struct _t_g{ struct _t_f *fp; int a; }; struct _t_f{ struct _t_g *gp; int a; }; typedef struct _t_g G; typedef struct _t_f F; union AA { }; union BB { int a; int b; int c; }; union CC { char a; int b; int c; }; void test_struct1() { struct A m_a; struct B m_b; struct C m_c; union AA m_aa; union BB m_bb; union CC m_cc; printf("sizeof(struct A) = %dn", sizeof(m_a)); printf("sizeof(struct B) = %dn", sizeof(m_b)); printf("sizeof(struct C) = %dn", sizeof(m_c)); printf("sizeof(struct AA) = %dn", sizeof(m_aa)); printf("sizeof(struct BB) = %dn", sizeof(m_bb)); printf("sizeof(struct CC) = %dn", sizeof(m_cc)); m_bb.a = 10; printf("m_bb.a = %d, m_bb.b = %d, m_bb.c = %dn", m_bb.a, m_bb.b, m_bb.c); m_cc.b = 20; if(m_cc.a) printf("little-endiann"); else printf("big-endiann"); printf("m_cc.a = %d, m_cc.b = %d, m_cc.c = %dn", m_cc.a, m_cc.b, m_cc.c); } int main() { test_struct1(); return 0; }
makefile:
CC = gcc CFLAGS = -g -Wall -O main:test.o $(CC) $^ -o $@ %.o:%.c $(CC) $(CFLAGS) -c $^ clean: rm -rf test.o


![[C语言]struct和union的区别 大小端 字节对齐详解 [C语言]struct和union的区别 大小端 字节对齐详解](http://www.mshxw.com/aiimages/31/977214.png)
