文章目录

  • 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语言:指针的偏移步长、结构体成员的偏移量、嵌套结构体成员的偏移量、结构体的内存对齐相关推荐

  1. c语言 指针的偏移计算

    文章目录 前言 一.指针偏移的概述: 二.指针偏移的应用: 总结 前言 指针可以进行加减运算,且只能进行加减计算,叫做指针偏移.今天我们就讲解一下什么是指针偏移. 一.指针偏移的概述: 前面说指针的类 ...

  2. 简单理解结构体的内存对齐

    学习总结 内存对齐三原则: 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子 ...

  3. c语言---指针结构体篇

    [前言]本系列(初阶)适用于初学者课前预习或者课后复习资料,包含了大部分基础知识点梳理与代码例方便看官理解,有问题请指出.本人邮箱地址:p772307283@outlook.com 可爱捏 目录 1. ...

  4. 【C语言指针】 指针+-整数、指针-指针、解引用、指针数组、二级指针、结构体声明、初始化、传参

    目录 一.指针 1.指针是什么 1.1.一个单元1个字节 2.指针和指针类型 2.1.指针类型的意义 ① 解引用 ② + -整数 例:把每个整形里放1 每个字节里放1 总结: 3.野指针 3.1.野指 ...

  5. C语言指针与结构体详述

    C语言指针与结构体详述 前言 一.数组概述 1.1 一维数组 1.2 二维数组 1.3 多维数组 二.字符数组和字符串 2.1 字符数组 2.2 字符串 2.3 字符串函数 2.4 拓展字符串函数 三 ...

  6. c语言指针变量输出不了共用体,瘋子C语言札记(结构体/共用体/枚举篇)

    瘋子C语言笔记(结构体/共用体/枚举篇) (一)结构体类型 1.简介: 例: struct date { int month; int day; int year; }; struct student ...

  7. C语言结构体与指针ppt,c语言指针和结构体:链表详解.ppt

    c语言指针和结构体:链表详解.ppt 1,第十一章 链表,2,例跳马.依下图将每一步跳马之后的位置x,y放到一个"结点"里,再用"链子穿起来",形成一条链,相邻 ...

  8. c语言指针和结构体(B站鹏哥讲解版)

    一.指针初阶 1.指针类型决定了:指针解引用的权限有多大. char 型改变两个字节,int改变8个字节. 2.指针类型决定了指针走一步走多远. int加4,char加1,double加8 3. 4. ...

  9. C语言指针结构体详解,结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过"结构体变量名.成员名"的方式引用结构体变量中的成员,除了这种方法之外还可以使用 ...

最新文章

  1. fsum函数测试以及分析
  2. 015_SpringBoot视图层技术thymeleaf-URL表达式
  3. 看完后,我才明白 Redis 为什么默认 16 个数据库?
  4. idm 服务器响应显示您没有权限下载此文件_仅需10分钟,让你掌握下载神器IDM的使用技巧...
  5. 农场js_通过销售农场商品来解释Web API
  6. 【Java】ArrayList 为啥要实现 RandomAccess 接口
  7. 谭浩强C语言第四版第九章课后习题7--9题(建立,输出,删除,插入链表处理)...
  8. HelloKafka入门 helloKafka
  9. 吉利车机安装第三方软件教程(win10笔记本详细操作方法)
  10. 三张图看懂 clientheight、offsetheight、scrollheight
  11. SCSI设备IO栈与块设备并发机制
  12. 计算机设计大赛材料准备讲解
  13. 图像处理基础知识——图片存储形式、色域、图片文件格式
  14. Hander创建消息
  15. camera知识储备2
  16. 【转】微信小游戏开发总结
  17. 输入半径计算圆的周长和面积
  18. SCI论文参考文献介绍
  19. 如何使用 Landsat 8 卫星影像计算地表温度
  20. oracle清理undo空间,删除Oracle Undo表空间

热门文章

  1. 达人评测 i51240p和r55625u哪个好
  2. bind server.c
  3. linux 串口 数量限制,linux – 你的Unix的TTY主要数量是多少?
  4. 苹果X可以升级5G吗_苹果分析师:2020 年的 iPhone 屏幕尺寸升级,还有 5G
  5. 营销之父科特勒万字演讲实录:营销的未来(含PPT和视频回放)
  6. PHP 图片去除水印 去除logo
  7. 嵌入式学习之STM32实现OLED
  8. unity 移动开发优化二 图形优化,脚本优化概述
  9. python股票全套系统_用python来炒股三 炒股交易系统(法则)
  10. 字体颜色 * 博客 * 好看