1.8结构体变量的定义和初始化✈️
struct S { int a; char c; }s1; struct S s3;//这样定义也是全局变量 struct B { float f; struct S s; }; int main()//在这里定义的全是局部变量 { //int arr[10] = {1,2,3}; //int a = 0;//有了值才有确定性 //结构体怎么初始化呢? struct S s2 = {100, 'q'}; struct B sb = { 3.14f, {200, 'w'}};//结构体的嵌套初始化正常来说要按照顺序, //不按照顺序初始化 struct S s3 = {.c = 'r', .a = 2000};//指定顺序 printf("%f,%d,%c\n", sb.f, sb.s.a, sb.s.c);//结合体成员的访问,后面两个是因为是嵌套了一个结构体 return 0; }
另一个例子:
struct S { char c; char arr[10]; int* p; }s1, s2; struct S s3; struct B { int a; struct S s; double d; }sb1, sb2; struct B sb3; int main() { struct S s4 = {'c', "zhangsan", NULL}; int a = 10; struct B sb4 = { 100, {'q', "lisi", &a}, 3.14}; return 0; }
1.9结构体内存对齐🚁
❗ 该如何计算结构体的大小:结构体内存对齐🐔🏀👖
这也是一个特别热门的考点: 结构体内存对齐
#include<stdio.h> struct S1 { int a; char c; }; struct S2 { char c1; int a; char c2; }; struct S3 { char c1; int a; char c2; char c3; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); printf("%d\n", sizeof(struct S3)); return 0; }
运行结果:
按以往的知识来说,从S1到S3的值分别推测为5,6,7,可是结果却不是这样的,并且S3比S2还多了一个成员变量,但是它们的大小却是相等的
为什么呢?计算结构体,需要用到以下规则
接下来,需要详细说明一下结果是如何求出来的
1.9.1先放int,再放char📣
struct S1 { int a; char c; }; int main() { printf("%d\n", sizeof(struct S1)); return 0; }
1.9.2调换顺序:先放char,再放int🔈
那么我们讲char c和int a的位置调换一下,结果又会不会不一样呢?
struct S1 { char c; int a; }; int main() { printf("%d\n", sizeof(struct S2)); return 0; }
c和a之间真的浪费了3个字节的空间吗?
#include<stdio.h> #include <stddef.h> struct S { char c1; int a; }; int main(){ struct S s = {0}; printf("%d\n", offsetof(struct S, c1));//0 printf("%d\n", offsetof(struct S, a));//4 return 0; }
🔎如何计算两个结构体变量的偏移量差距🔎
方法1:取地址计算,4和8之间差4
可以发现它们之间差了4个字节
方法2:offsetof函数
使用之前要引用一下头文件
可以看到是返回成员变量的偏移量
执行效果:
1.9.3先放两个char,再放一个int🔏
struct S2 { char c1; char c2; int i; }; int main() { printf("%d\n", sizeof(struct S2)); return 0; }
1.9.4先放一个double,再放一个char,最后放一个int💡
struct S3 { double d; char c; int i; }; int main() { printf("%d\n", sizeof(struct S3)); return 0; }
执行效果:
1.9.5嵌套结构体计算 🔦
struct S3 { double d; char c; int i; }; struct S4 { char c1; struct S3 s3; double d; }; int main() { //计算嵌套结构体总结构体的大小 printf("%d\n", sizeof(struct S4)); return 0; }
1.9.6回到整个题目🔨
#include<stdio.h> struct S1 { int a; char c; }; struct S2 { char c1; int a; char c2; }; struct S3 { char c1; int a; char c2; char c3; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); printf("%d\n", sizeof(struct S3)); return 0; }
为什么存在内存对齐?
大部分的参考资料都是如是说的:
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。
取决于机器字长
如果是32位机器,一次读写读32bit,那就是4byte
总结:
1.对于一个int 类型,若是按照内存对齐来存储,处理器只需要访存一次就可以读取完4个字节
2.若没有按照内存对其来读取,如上图所示,就需要访问内存两次才能读取出一个完整的int 类型变量
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起
#include<stdio.h> struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
调整一下顺序就可以减少空间的浪费
2.0修改默认对齐数⭕
之前我们见过了 #pragma这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。
#pragma pack(1)//设置默认对齐数为1 struct S { char c1;//1 1 1 int i; //4 1 1 char c2;//1 1 1 }; #pragma pack()//取消设置的默认对齐数,还原为默认 int main() { printf("%d\n", sizeof(struct S)); return 0; }
因为它们每一个对齐数都是1,所以只要是1的倍数的偏移量位置,它们都可以存放,所以就不会有空余的内存
结论:
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。(我们不想对齐的时候就把对齐数设置成1)