1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。
2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。
3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的"最宽基本类型成员"的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。
4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。
1.结构体的大小计算->内存对齐。
1.1 普通结构体的内存对齐
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//结构体的成员的排列方式会影响到占用的字节数的大小
typedef struct information{
int a;
char b;
int c;
char d;
} Info;
typedef struct netlink{
char b;
char d;
int a;
int c;
} Netk;
/*上面的两个结构体,每次申请的字节数都是其成员中占用字节最大的,如char 与 int 则每次申请的大小
是sizeof(int),若一个结构体里有int char double则每次申请的大小就是sizeof(double).
以上面两个结构体为例,存放规则是:
每次申请sizeof(int)
int a 刚好占满sizeof(int)
第二次申请sizeof(int) 存放了 char b,它只占了1个字节,但是紧接着的c也要占4个字节,因为“成员变
量”的首地址必须是该成员变量类型大小的整数倍,所以后面3个字节空着不用,再申请4个字节,存放c
由于c占满了4个字节,此时再次申请4个字节存放d,所以在没有任何外界优化的情况下,sizeof(Info)是16
对于结构体netlink, 每次申请依然是4个字节,第一次申请的存放b,但是b只占了1个字节,接下来的地址肯
定是1的整数倍,所以可以存放d, 然后再分别申请2次,存放a,d 所以sizeof(Netk)是12
对于这样的结构体,可以使用此方法进行计算。
*/
int main(){
printf("sizeof(struct information)=[%ld]\n", sizeof(Info));
printf("sizeof(struct netlink )=[%ld]\n", sizeof(Netk));
return 0;
}
1.2 含有指针的结构体内存对齐
#include <stdio.h>
typedef struct infomation{
char a;
char b;
char *c;
} Info;
typedef struct netlink{
int a;
int b;
int *c;
} Netk;
/*此时结构体每次申请的字节数是sizeof(c),在64位系统中,指针占8字节
所以结构体大小是16, 即指针成员变量也对内存对齐有影响(上面的指针是
野指针,下面会详细说明)
*/
int main(){
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); //16
printf("sizeof(Netk)=[%ld]\n", sizeof(Netk)); //16
return 0;
}
1.3 结构体嵌套后的内存对齐
#include <stdio.h>
union netlink{
int a;
char b;
double c;
};
typedef struct information{
char i; //[第一次8字节的第一个字节8-1]
int u; //[8-4]
int r; //[第二次申请的8字节]
union netlink n; //[第三次申请的8字节]
} Info;
/*每次申请空间为double类型[8字节],结构体给出占用大小
具体不再赘述
即:联合体的数据会影响到宿主结构体的内存分配方式
*/
int main(){
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); //24
printf("sizeof(union netlink)=[%ld]\n", sizeof(union netlink)); //8
return 0;
}
1.4 结构体与联合体的嵌套后内存对齐
union us{
int a;
char b;
double c;
};
typedef struct information{
union us u;
int r;
char s;
int t;
} Info;
/*
联合体成员变量的“字”对结构体也会产生影响。
对于结构体information,每次申请的空间需要按照联合体中的double类型来判断。
即每次申请8字节,第一次申请8字节存放了联合体,然后申请8字节存放了成员变量r,
由于r占用4 字节,还剩下4字节,此时可以把s 也放进去,然后剩余的空间不足以存放
int 类型的 t 会再次申请8 字节,所以大小最终是24
*/
int main(){
printf("sizeof(union us )=[%ld]\n", sizeof(union us));
printf("sizeof(information)=[%ld]\n", sizeof(Info ));
return 0;
}
1.5 结构体中的位对齐与强制下一位对齐
#include <stdio.h>
typedef struct information{
double a;
int b;
char c;
int :0;
char d;
} Info;
/*每次申请8字节,但是int:0;强制使结构体从下一个[int地址的整数倍]开始对齐
如果没有int :0; 结构体大小是16,由于存在int :0;在变量c占用后,d变量被强制
从下一个int类型大小的整数倍开始对齐,所以d 跟 c的地址差了3个字节,此时需要
额外申请一个double大小的内存,即information占24字节
*/
int main(){
Info in;
memset(&in, 0x00, sizeof(Info));
//从打印的成员变量的地址看,地址偏移量为以下所列出来的值[16进制转换为10进制]
printf("in.a=[%p]\n", &in.a); //0
printf("in.b=[%p]\n", &in.b); //0+8
printf("in.c=[%p]\n", &in.c); //0+8+4
printf("in.d=[%p]\n", &in.d); //0+8+4+1+3 [本来3字节足够存放元素d,由于强制对齐int:0,剩下的元素不足4字节,所以需要再次申请double的大小,d元素占用第三次申请的double的第一个字节]
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); //24
return 0;
}
1.6 结构体末尾放不定数组与指针时的结构体大小计算
#include <stdio.h>
typedef struct information{
int a;
int b;
char buf[];
} Info;
//空的数组并不占用空间。
int main(){
printf("sizeof(Info)=[%d]\n", sizeof(Info)); //8
return 0;
}
1.7 位对齐
#include <stdio.h>
typedef struct information{
int a;
int b:16;
char c;
char d;
} Info;
//位对齐会强制指定的成员变量占用指定的位宽,本来int要占4个字节,上面的结构体只让它占用了16bit也就是2字节,所以第二次申请的4字节剩下的2个字节可以用来存放变量c和d 结构体大小是8
int main(){
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); //8
return 0;
}
1.8 联合体中嵌套了结构体
#include <stdio.h>
typedef struct information{
char a;
int b;
char c;
} Info;
typedef union onion{
Info in;
double d;
}Onion;
/*联合体中内嵌了结构体,首先计算结构体的大小,但是结构体大小的计算需要参考
联合体的成员中最大的成员的字节数,上面的结构体,虽然自己的成员变量最大的
字是int,但是申请空间的时候却是按照联合体的double的大小申请的,最终大小取
结构体大小与联合体单个最大的成员中的最大值
*/
int main(){
Onion on;
printf("sizeof(Onion)=[%ld]\n", sizeof(Onion)); //16
printf("address as following\n");
printf("on.in.a =[%p]\n", &on.in.a); //0
printf("on.in.b =[%p]\n", &on.in.b); //0+4
printf("on.in.c =[%p]\n", &on.in.c); //0+8
printf("on.d =[%p]\n", &on.d ); //0
return 0;
}
2.结构体末尾的不定数组问题
2.1 不定数组在结构体计算占用字节数的时候不占任何空间,但是不同类型的数组对申请时的字节大小有影响。
#include <stdio.h>
typedef struct information{
int a;
int b;
int c;
char buf[];//这样的数组必须放结构体末尾
} Info;
typedef struct netlink{
int a;
int b;
int c;
double d[];
} Netk;
//空的字符数组在结构体末尾并不占用空间,但是会影响结构体每次申请的内存字节数
int main(){
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); // 12-每次申请int类型的字节,申请了3次
printf("sizeof(Netk)=[%ld]\n", sizeof(Netk)); // 16-每次申请double类型的字节,申请2次
return 0;
}
3.结构体末尾放一个指针与不定数组的区别
#include <stdio.h>
typedef struct information{
double a;
double b;
double c;
char buf[];
} Info;
typedef struct netlink{
double a;
double b;
double c;
char *buf;
} Netk;
/* 1.结构体末尾的指针占用空间[8字节],并且会影响每次申请空间的大小[有兴趣的同学可以把结构体的double都换成比double小的类型测试]
2.结构体末尾的空数组,地址与前一个变量是紧邻的,而结构体末尾的指针是一个“野指针”
*/
int main(){
printf("sizeof(Info)=[%ld]\n", sizeof(Info)); // 24
printf("sizeof(Netk)=[%ld]\n", sizeof(Netk)); // 32
Info in;
Netk ne;
printf("in.c =[%p]\n", &in.c); // 0
printf("in.buf =[%p]\n", in.buf); // 0+8
printf("ne.c =[%p]\n", &ne.c); // 0
printf("ne.buf =[%p]\n", ne.buf); //null
return 0;
}