1 不同类型指针的偏移步长
(1)不同类型的指针 +1 时,内存偏移的字节数不同。
char *类型指针 + 1,内存偏移 1 字节;
int *类型指针 + 1,内存偏移 4 字节;
double *类型指针 + 1,内存偏移 8 字节。
示例:
#include <stdio.h>
//1.不同类型的指针+1时,偏移的字节数不同
void func1() {
char* p = NULL;
printf("%p\n", p); //00000000
printf("%p\n", p + 1); //00000001
int* pp = NULL;
printf("%p\n", pp); //00000000
printf("%p\n", pp + 1); //00000004
}
(2)对不同类型的指针解引用时,取出的字节数不同。
示例:
#include <stdio.h>
#include <string.h>
//2.对不同类型的指针解引用时,取出的字节数不同
void func2() {
char buff[1024] = { 0 };
int num = 1234;
//memset(buff, 0, sizeof(buff));
memcpy(buff, &num, sizeof(num));
char* p = buff;
//将char*指针强转为int*类型指针后,再解引用
printf("%d\n", *(int *)p); //1234
}
//3.对不同类型的指针解引用时,取出的字节数不同
void func3() {
char buff[1024] = { 0 };
int num = 1234;
//memset(buff, 0, sizeof(buff));
memcpy(buff + 1, &num, sizeof(num));
char* p = buff;
//将char*指针强转为int*类型指针后,再解引用
printf("%d\n", *(int*)(p + 1)); //1234
}
2 结构体成员的偏移量
头文件
<stddef.h>定义了标准宏。
offsetof()宏可计算结构体成员的偏移量:offsetof(结构体类型名 , 结构体成员名)。
例:offsetof(struct Object, field);
示例:
#include <stdio.h>
#include <stddef.h> //使用标准宏
//结构体内存对齐(按数据类型长度最大的对齐-double 8字节对齐)
struct Object {
char a; //0 ~ 3
int b; //4 ~ 7
char buff[64]; //8 ~ 71
double c; //72 ~ 79
};
int main() {
struct Object obj = { 'a', 1, "hello world", 3.14 };
/*
offsetof()宏可计算结构体成员的偏移量(需包含<stddef.h>头文件)
offsetof(结构体类型名 , 结构体成员名)
*/
printf("%d\n", offsetof(struct Object, a)); //0
printf("%d\n", offsetof(struct Object, b)); //4
printf("%d\n", offsetof(struct Object, buff)); //8
printf("%d\n", offsetof(struct Object, c)); //72
/*
通过结构体成员的指针偏移,可访问各个结构体成员
将指向结构体的指针强转为(char *)类型,每次指针偏移的步长为1
*/
struct Object* p = &obj;
//char*类型的起始地址,加上结构体成员的偏移量,再强转为指定结构体成员对应的指针类型,解引用获取指定结构体成员的值
printf("%c\n", *( (char*)p + offsetof(Object, a) )); //a (ASCII码 97)
printf("%d\n", *(int *)( (char*)p + offsetof(Object, b) )); //1
//打印输出字符串类型时,无需解引用,使用字符串的首地址即可(默认至'\0'结束)
printf("%s\n", ( (char*)p + offsetof(Object, buff) )); //hello world
printf("%lf\n", *(double *)( (char*)p + offsetof(Object, c) )); //3.140000
//通过结构体成员的偏移地址,解引用并修改对应结构体成员的值
*(double *)((char*)p + offsetof(struct Object, c)) = 3.1415926; //修改结构体成员c的值
printf("%lf\n", *(double *)((char*)p + offsetof(Object, c))); //3.141593
return 0;
}
3 嵌套结构体成员的偏移量
#include <stdio.h>
#include <stddef.h>
struct Inner {
char a;
int b;
};
struct Outer {
char c;
int d;
struct Inner inner;
};
int main() {
struct Outer outer = { 'a', 1, {'b', 2} };
/* 获取Outer结构体中嵌套的Inner结构体成员b */
//1.通过结构体变量访问
printf("%d\n", outer.inner.b); //2
//2.通过2次指针偏移:
//(1)先获取嵌套结构体成员inner相对outer的偏移
int offset1 = offsetof(struct Outer, inner);
//(2)再获取属性b相对结构体inner的偏移
int offset2 = offsetof(struct Inner, b);
printf("%d\n", *(int*)((char*)&outer + offset1 + offset2)); //2
//3.通过1次指针偏移,获取指向嵌套结构体inner的指针,通过结构体指针访问成员
printf("%d\n", ((struct Inner*)((char*)&outer + offset1))->b); //2
return 0;
}
4 结构体的内存对齐
4.1 内存对齐的原因与优点
内存未对齐的问题:CPU按块读取内存,当CPU访问数据时,若内存未对齐,则可能导致二次访问的情况,即前后两次访问的数据需拼接后才能获取指定数据。
内存对齐的优点:以空间换时间,浪费部分用于对齐的空间,通过一次访问即可获取指定数据。
4.2 结构体内存对齐的规则
(1)内置数据类型:数据存储在该类型大小的整数倍上。
(2)自定义数据类型:依照特定的对齐规则。
①从第1个属性开始,从 偏移量0位置 开始存储;
②从第2个属性开始,从 该数据类型大小 和 对齐模数 的较小值的整数倍开始存储。即min {该数据类型大小 , 对齐模数} 的整数倍。
③整体计算完毕后进行二次对齐,结构体的总大小必须是 该结构体中最大数据类型 和 对齐模数 的 较小值的整数倍,不足需补齐。即min {该结构体中最大数据类型 , 对齐模数} 的整数倍。
注:使用
#pragma pack(show),生成代码后查看”杂注”即默认对齐模数。默认对齐模数为8。
例:pragma pack(show) 的值 == 8
示例1:按默认对齐模数(8)对齐
#include <stdio.h>
//#pragma pack(show) //对齐模数默认为8 //生成后查看
typedef struct {
int a; //0 ~ 3
char b; //4 → 4 ~ 7 //double对齐后,补为4 ~ 7
double c; //8 ~ 15
float d; //16 ~ 19 → 16 ~ 23 //二次对齐,补为16 ~ 23
} StructA;
int main() {
printf("%d\n", sizeof(StructA)); //24
return 0;
}
示例2:按指定对齐模数(1)对齐
#include <stdio.h>
#pragma pack(1) //对齐模数为1
typedef struct {
int a; //0 ~ 3
char b; //4 //对齐模数为1,无需补齐
double c; //5 ~ 12
float d; //13 ~ 16 //二次对齐,对齐模数为1,无需补齐
} StructB;
int main() {
printf("%d\n", sizeof(StructB)); //17
return 0;
}
4.3 结构体嵌套结构体时的对齐规则
①从第1个属性开始,从 偏移量0位置 开始存储;
②从第2个属性开始的非嵌套结构体属性,从 该数据类型大小 和 对齐模数 的较小值的整数倍开始存储。即min {该数据类型大小 , 对齐模数} 的整数倍。
③从第2个属性开始的嵌套结构体属性,从 该嵌套结构体中最大数据类型 和 对齐模数 的较小值的整数倍开始存储。即min {该嵌套结构体中最大数据类型 , 对齐模数} 的整数倍。
④整体计算完毕后进行二次对齐,结构体的总大小必须是 该结构体中最大数据类型 和 对齐模数 的 较小值的整数倍,不足需补齐。即min {该结构体中最大数据类型 , 对齐模数} 的整数倍。
示例:结构体嵌套结构体时,按默认对齐模数(8)对齐
#include <stdio.h>
#include <stddef.h>
//默认对齐模数为8
typedef struct {
int a; //0 ~ 3
char b; //4 → 4 ~ 7 //double对齐后,补为4 ~ 7
double c; //8 ~ 15
float d; //16 ~ 19 → 16 ~ 23 //二次对齐,补为16 ~ 23
} Inner; //对齐模数为8时,二次对齐后,Inner结构体的大小为24
typedef struct {
int x; //0 ~ 3
char y; //4 → 4 ~ 7 //嵌套结构体inner对齐后,补为4 ~ 7
Inner inner; //8 ~ 31 //按嵌套结构体inner的最大数据类型(8)和对齐模数 对齐
float z; //32 ~ 35 → 32 ~ 39 //二次对齐,补为32 ~ 39
} Outer; //对齐模数为8时,二次对齐后,Outer结构体的大小为40
int main() {
printf("内层结构体大小:%d\n", sizeof(Inner)); //24
printf("外层结构体大小:%d\n", sizeof(Outer)); //40
printf("属性x的偏移:%d\n", offsetof(Outer , x)); //0
printf("属性y的偏移:%d\n", offsetof(Outer , y)); //4
printf("属性inner的偏移:%d\n", offsetof(Outer , inner)); //8
printf("属性z的偏移:%d\n", offsetof(Outer , z)); //32
return 0;
}

824

被折叠的 条评论
为什么被折叠?



