结构和联合
# 1 声明
# 1.1 结构声明
struct tag { member-list } variable-list;
tag是标签,采用typedef可以创建一种新的类型,可以直接用类型名声明变量。结尾处的是类型名。
struct Teacher
{
char name[50];
int age;
};
struct Teacher t1;
//通过typedef把struct Teacher3改名为Teacher3
typedef struct Teacher3
{
char name[50];
int age;
}Teacher3;
Teacher3 t9;
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
在C中,这个申明后申请结构变量的方法有两种:
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
在c++中可以有
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
(3)tagMyStruct 变量名
# 1.2 不完整声明(前置声明)
两个结构体互相依赖,需要使用不完整声明,它声明一个作为结构标签的标识符。
struct B;
struct A {
struct B *partner;
};
struct B {
struct A *partner;
};
警告:
// 警惕下面这个陷阱 typedef struct { int a; SELF_REF3 *b; int c; } SELF_REF3; // 上面声明的目的是为这个结构创建类型名为SELF_REF3,但是类型名直到声明的末尾才定义,所以在结构声明的内部它尚未定义。 // 解决方案是定义一个结构标签来声明b,如下所示: typedef struct SELF_REF3_TAG { int a; struct SELF_REF3_TAG *b; int c; } SELF_REF3;
# 2 结构体成员偏移量
int offsize1 = (int)&(p->age) - (int)p; // age相对于结构体 Teacher的偏移量
int offsize2 = (int)&(((Teacher_t *)0)->age );//绝对0地址 age的偏移量 假设地址0处存放的是一个type类型的结构体变量,这样的话这个结构体变量的基址就是0,所以结构体成员变量的地址的大小在数值上就等于该结构体成员在结构体中的偏移量。
# 内存对齐的原则
默认情况下,数据成员的对齐规则(以最大的类型字节为单位)。
字节对齐可以通过程序控制,采用指令:
#pragma pack(xx)
#pragma pack(1) //1字节对齐
#pragma pack(2) //2字节对齐
#pragma pack(4) //4字节对齐
#pragma pack(8) //8字节对齐
#pragma pack(16) //16字节对齐
# 3 结构体赋值/拷贝
- 结构体直接赋值,是将结构体中的值直接进行拷贝,如果结构体的成员都是分配在栈区,则有效;若存在分配于堆区的成员,则该方法无法实现深拷贝,即指向同一块内存区域。—— 浅拷贝
- 为结构体动态分配内存,如果结构体中存在指针,需要为指针分配内存。
- 结构体中嵌套指针,如果想执行深copy,再显示的分配内存
void deepCopyTeacher(Teacher_t *to, Teacher_t *from)
{
*to = *from;
to->pname2 = (char *)malloc(100);
strcpy(to->pname2, from->pname2);
//memcpy(to, from , sizeof(Teacher_t));
}
// 结构体作为参数传给函数时,尽量采用指向结构的指针传递给函数,效率会更高。
# 4 联合
联合的所有成员引用的是内存中相同的位置,把不同的东西存储与同一个位置时,使用联合。
union {
float f;
int i;
} fi;
// 变量fi只占据内存的32位,如果成员f被使用,这个字就作为浮点值访问,如果成员i被使用,这个字就作为整型值访问, 区别是每个成员的类型决定了这些位被如何解释
fi.f = 3.14159;
printf("%d\n", fi.i);