C语言:指针的偏移步长、结构体成员的偏移量、嵌套结构体成员的偏移量、结构体的内存对齐
文章目录
- 1 不同类型指针的偏移步长
- 2 结构体成员的偏移量
- 3 嵌套结构体成员的偏移量
- 4 结构体的内存对齐
- 4.1 内存对齐的原因与优点
- 4.2 结构体内存对齐的规则
- 4.3 结构体嵌套结构体时的对齐规则
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); //00000000printf("%p\n", p + 1); //00000001int* pp = NULL;printf("%p\n", pp); //00000000printf("%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 ~ 3int b; //4 ~ 7char buff[64]; //8 ~ 71double 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)); //0printf("%d\n", offsetof(struct Object, b)); //4printf("%d\n", offsetof(struct Object, buff)); //8printf("%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 worldprintf("%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.141593return 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); //2return 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 ~ 3char b; //4 → 4 ~ 7 //double对齐后,补为4 ~ 7double c; //8 ~ 15float d; //16 ~ 19 → 16 ~ 23 //二次对齐,补为16 ~ 23
} StructA;int main() {printf("%d\n", sizeof(StructA)); //24return 0;
}
示例2:按指定对齐模数(1)对齐
#include <stdio.h>
#pragma pack(1) //对齐模数为1typedef struct {int a; //0 ~ 3char b; //4 //对齐模数为1,无需补齐double c; //5 ~ 12float d; //13 ~ 16 //二次对齐,对齐模数为1,无需补齐
} StructB;int main() {printf("%d\n", sizeof(StructB)); //17return 0;
}
4.3 结构体嵌套结构体时的对齐规则
①从第1个属性开始,从 偏移量0位置 开始存储;
②从第2个属性开始的非嵌套结构体属性,从 该数据类型大小 和 对齐模数 的较小值的整数倍开始存储。即min {该数据类型大小 , 对齐模数} 的整数倍
。
③从第2个属性开始的嵌套结构体属性,从 该嵌套结构体中最大数据类型 和 对齐模数 的较小值的整数倍开始存储。即min {该嵌套结构体中最大数据类型 , 对齐模数} 的整数倍
。
④整体计算完毕后进行二次对齐,结构体的总大小必须是 该结构体中最大数据类型 和 对齐模数 的 较小值的整数倍,不足需补齐。即min {该结构体中最大数据类型 , 对齐模数} 的整数倍
。
示例:结构体嵌套结构体时,按默认对齐模数(8)对齐
#include <stdio.h>
#include <stddef.h>//默认对齐模数为8
typedef struct {int a; //0 ~ 3char b; //4 → 4 ~ 7 //double对齐后,补为4 ~ 7double c; //8 ~ 15float d; //16 ~ 19 → 16 ~ 23 //二次对齐,补为16 ~ 23
} Inner; //对齐模数为8时,二次对齐后,Inner结构体的大小为24typedef struct {int x; //0 ~ 3char y; //4 → 4 ~ 7 //嵌套结构体inner对齐后,补为4 ~ 7Inner inner; //8 ~ 31 //按嵌套结构体inner的最大数据类型(8)和对齐模数 对齐float z; //32 ~ 35 → 32 ~ 39 //二次对齐,补为32 ~ 39
} Outer; //对齐模数为8时,二次对齐后,Outer结构体的大小为40int main() {printf("内层结构体大小:%d\n", sizeof(Inner)); //24printf("外层结构体大小:%d\n", sizeof(Outer)); //40printf("属性x的偏移:%d\n", offsetof(Outer , x)); //0printf("属性y的偏移:%d\n", offsetof(Outer , y)); //4printf("属性inner的偏移:%d\n", offsetof(Outer , inner)); //8printf("属性z的偏移:%d\n", offsetof(Outer , z)); //32return 0;
}
C语言:指针的偏移步长、结构体成员的偏移量、嵌套结构体成员的偏移量、结构体的内存对齐相关推荐
- c语言 指针的偏移计算
文章目录 前言 一.指针偏移的概述: 二.指针偏移的应用: 总结 前言 指针可以进行加减运算,且只能进行加减计算,叫做指针偏移.今天我们就讲解一下什么是指针偏移. 一.指针偏移的概述: 前面说指针的类 ...
- 简单理解结构体的内存对齐
学习总结 内存对齐三原则: 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子 ...
- c语言---指针结构体篇
[前言]本系列(初阶)适用于初学者课前预习或者课后复习资料,包含了大部分基础知识点梳理与代码例方便看官理解,有问题请指出.本人邮箱地址:p772307283@outlook.com 可爱捏 目录 1. ...
- 【C语言指针】 指针+-整数、指针-指针、解引用、指针数组、二级指针、结构体声明、初始化、传参
目录 一.指针 1.指针是什么 1.1.一个单元1个字节 2.指针和指针类型 2.1.指针类型的意义 ① 解引用 ② + -整数 例:把每个整形里放1 每个字节里放1 总结: 3.野指针 3.1.野指 ...
- C语言指针与结构体详述
C语言指针与结构体详述 前言 一.数组概述 1.1 一维数组 1.2 二维数组 1.3 多维数组 二.字符数组和字符串 2.1 字符数组 2.2 字符串 2.3 字符串函数 2.4 拓展字符串函数 三 ...
- c语言指针变量输出不了共用体,瘋子C语言札记(结构体/共用体/枚举篇)
瘋子C语言笔记(结构体/共用体/枚举篇) (一)结构体类型 1.简介: 例: struct date { int month; int day; int year; }; struct student ...
- C语言结构体与指针ppt,c语言指针和结构体:链表详解.ppt
c语言指针和结构体:链表详解.ppt 1,第十一章 链表,2,例跳马.依下图将每一步跳马之后的位置x,y放到一个"结点"里,再用"链子穿起来",形成一条链,相邻 ...
- c语言指针和结构体(B站鹏哥讲解版)
一.指针初阶 1.指针类型决定了:指针解引用的权限有多大. char 型改变两个字节,int改变8个字节. 2.指针类型决定了指针走一步走多远. int加4,char加1,double加8 3. 4. ...
- C语言指针结构体详解,结构体指针,C语言结构体指针详解
结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过"结构体变量名.成员名"的方式引用结构体变量中的成员,除了这种方法之外还可以使用 ...
最新文章
- fsum函数测试以及分析
- 015_SpringBoot视图层技术thymeleaf-URL表达式
- 看完后,我才明白 Redis 为什么默认 16 个数据库?
- idm 服务器响应显示您没有权限下载此文件_仅需10分钟,让你掌握下载神器IDM的使用技巧...
- 农场js_通过销售农场商品来解释Web API
- 【Java】ArrayList 为啥要实现 RandomAccess 接口
- 谭浩强C语言第四版第九章课后习题7--9题(建立,输出,删除,插入链表处理)...
- HelloKafka入门 helloKafka
- 吉利车机安装第三方软件教程(win10笔记本详细操作方法)
- 三张图看懂 clientheight、offsetheight、scrollheight
- SCSI设备IO栈与块设备并发机制
- 计算机设计大赛材料准备讲解
- 图像处理基础知识——图片存储形式、色域、图片文件格式
- Hander创建消息
- camera知识储备2
- 【转】微信小游戏开发总结
- 输入半径计算圆的周长和面积
- SCI论文参考文献介绍
- 如何使用 Landsat 8 卫星影像计算地表温度
- oracle清理undo空间,删除Oracle Undo表空间
热门文章
- 达人评测 i51240p和r55625u哪个好
- bind server.c
- linux 串口 数量限制,linux – 你的Unix的TTY主要数量是多少?
- 苹果X可以升级5G吗_苹果分析师:2020 年的 iPhone 屏幕尺寸升级,还有 5G
- 营销之父科特勒万字演讲实录:营销的未来(含PPT和视频回放)
- PHP 图片去除水印 去除logo
- 嵌入式学习之STM32实现OLED
- unity 移动开发优化二 图形优化,脚本优化概述
- python股票全套系统_用python来炒股三 炒股交易系统(法则)
- 字体颜色 * 博客 * 好看