一、结构体详解
1.结构体概述
结构体作用 结构体是一种自定义数据类型,可以解决数组类型单一的缺陷,即定义的结构体类型可以包含多种数据类型;结构体的存储 结构体在内存中是一块连续存储的空间,类似于多维数组,因此有下标和指针两种访问方式:
struct student
{
char *name
;
int num
;
double score
;
}s1
;
int main(void)
{
struct student
*s2
= &s1
;
s1
.name
= "s1";
s1
.num
= 1;
s2
->score
= 3.14;
printf("s1.name = %s\n", s1
.name
);
printf("s1.num = %d\n", *((int *)((int)&s1
+ sizeof(char *))));
printf("s1.score = %f\n", s2
->score
);
}
结构体变量名 结构体变量名代表的是整个结构体,因此无论是值还是地址,都代表着整个结构体;
struct str
{
char a
;
char c
;
};
int main()
{
struct str s1
= {
s1
.a
= 'a',
s1
.c
= 'a'
};
printf("&s1 = %p\n", &s1
);
printf("s1 = %x\n", s1
);
printf("sizeof(s1) = %d\n", sizeof(s1
));
printf("&s1.a = %p\n", &s1
.a
);
}
2.结构体对齐访问
对齐访问的原因 硬件、外设、Cache等都要求四字节访问可以提高访问效率;
对齐访问规则 (1)32bit编译器默认4byte访问:结构体整体大小必须4字节对齐(4的倍数); (2)结构体中的元素也必须对齐存放:元素首地址为是4的倍数,结束地址取决于下一个元素大小:若两个元素大小相加可以填充,则组合起来需要4字节对齐; (3)编译器考虑结构体存放时,以满足以上要求的最少内存需要的排布来算。
字节对齐的指令 (1)#pragma pack() 和#pragma pack(n):以#pragma pack(n)为始,以#pragma pack()为止的范围内的结构体成员按n字节对齐:
#pragma pack(1)
struct stu
{
char c
;
int num
;
};
#pragma pack()
sizeof(stu
) == 5;
(2)__attribute__((packed)) 和__attribute__((aligned(n))):直接放在对齐类型后义,范围是当前__attribute__((packed))或__attribute__((aligned(n)))的类型,作用域结构体整体而非单独的成员;__attribute__((aligned(n))):使结构体整体以n字节对齐;__attribute__((packed)):取消结构体整体的对齐方式,即结构体实际占用空间大小;
struct stu
{
char c
;
int num
;
}__attribute__((aligned(4));
sizeof(stu
) == 8;
struct stu
{
char c
;
int num
;
}__attribute__((packed
));
sizeof(stu
) == 5;
二、共用体详解
共用体概述
共用体类型、变量的定义与结构体相同,使用方法与结构体类似;共用体内部所有成员共用一个内存空间,因此对一个成员赋值即对所有成员赋值;各成员之间的区别只是数据类型不一样即解析的方式不一样;共用体的大小等于成员中占最大内存的数据类型的大小,且由于共用一个内存空间,因此不涉及字节对齐;
union stu
{
char a
;
int b
;
float c
;
}s1
;
int main(void)
{
union stu s1
;
s1
.b
= 97;
printf("s1.a = %c\n", s1
.a
);
printf("s1.b = %d\n", s1
.b
);
printf("s1.c = %f\n", s1
.c
);
printf("sizeof(s1) = %d\n", sizeof(s1
));
};
共用体本质上也可以通过指针和强制类型转换来代替
char *pc
= &a
;
printf("s1.b = %d\n", *((int *)pc
));
三、枚举详解
1.枚举概述
枚举的作用 枚举通过声明符号来表示整型常量,来提高代码的可读性和直观,通常用在有些变量的取值被限定在一个有限的范围内,如一周从周一到周日,每月1号到31号等;定义 枚举的语法与结构体类似
enum color
{
red,
green
,
blue
=4,
yellow,
}c1
;
int func1()
{
enum color c2
;
c1
= red
;
}
枚举符 (1)枚举类型的成员称为枚举符(如red、yellow等),枚举符本质是整型常量,因此可以用枚举符代替所有使用整型常量的地方,如声明数组的大小等:int str[yellow] = {0, 1, 2, 3}; (2)枚举符所代表的整型常量默认从0开始(如red == 0),依次向后排(如green默认为1);但可以自定义(如blue = -4),自定义之后的值根据自定义的值向后排(如yellow = -5);
2.枚举与宏定义区别
相同点 枚举与宏定义都可以通过字符来代替某个常量,是代码更直观,二者之间大部分情况下可以替换通用。不同点 枚举通常用在有顺序或某些常量的有限集合中,而宏定义更多用在独立的,相互之间无关联的定义之中。