【C语言】深度探索offsetof,解析结构体的成员数组和指针
目录
- 一.offsetof的功能
- 二.自定义offsetof
- 三.探索结构体
- 3.1结构体中的成员
- 3.2结构体指针和数组
该篇博客主要讲了自定义offsetof是如何实现的,为什么要这样写,以此沿生出对结构体偏移量产生的内存状态,和对结构体中指针和数组区别的探索,这里是给我启发和帮助的一篇博客-----C语言结构体里的成员数组和指针,这篇博客是一位行业大佬写的,正是这篇博客让我看到了在结构体中更深层的东西,建议大家去读一下原文。
一.offsetof的功能
用来确定结构体中各成员变量的偏移量。如果对这块知识不熟悉可以看结构体进阶详解这篇博客。
#include<stddef.h>typedef struct Student
{int age;char name[10];char sex[10];
}Stu;int main()
{printf("%d %d\n", offsetof(Stu, age),offsetof(Stu,name));return 0;
}
二.自定义offsetof
#define MY_offsetof(TYPE, MEMBER) (size_t)(&((TYPE*)0)->MEMBER)typedef struct Student
{int age;char name[10];char sex[10];
}Stu;int main()
{printf("%d %d\n", MY_offsetof(Stu, age),MY_offsetof(Stu,name));return 0;
}
- 这个时候大家应该会有很多的疑惑,为什么这样写就能得到成员变量对应的偏移量。
- 为什么要取地址
- 为什么又要对它进行强制类型转换
接下来让我们慢慢探索这些问题!
三.探索结构体
首先我们先看一个简单的代码:
typedef struct Student
{int age;char name[10];
}Stu;int main()
{Stu *st = NULL;if (st->name){printf(st->name);}return 0;
}
- 当我们将这段代码放入编译器,进行调试时,很容易发现,到12行时,就会报错。
- 那为什么在if语句里它能正确运行,而到了12行,却会报错呢?
这个问题先不急着回答,当我们对代码进行小小的修改,将printf函数改为如下形式:
printf("%d\n", st->name);
我们会得到数字4.
- 这就解释了为什么在if语句中可以运行通过了。
- 而在12行中,只写st->name只有在该成员变量存储为字符串的时候可以正常运行,而该代码明显不行。
- 新的问题就出现了,为什么会输出4呢?
3.1结构体中的成员
首先,我们要明白,变量,就是内存地址的一个抽象名字而已。在静态编译的程序中,所有的变量名都将转化为机器能够识别的地址。
所以有了——栈内存、堆内存、静态内存、常量内存,我们代码中的所有变量都会被编译器预先放到这些内存区中。
有了这些基础,我们来看一下如下结构体中的成员变量的地址是什么?
typedef struct Student
{int age;int phone[10];int* phone2;double name;
}Stu;Stu s;
如图是在VS2019中调试的结果,我们可以看到各成员变量的地址。
s的地址为起始地址,age的地址与s变量的地址相同,其余成员变量都是正常发生了偏移,是对于s的相对地址。
当我们输出这些地址时:
printf("%d\n", &(s->age));printf("%d\n", s->phone);printf("%d\n", &(s->phone2));printf("%d\n", &(s->name));
如此我们便得到了,各个成员变量的偏移量了,这也是为什么之前输出为4的原因,输出的为它的相对地址。
那我们在现在就会过头来在看一下自定义的offsetof:
#define MY_offsetof(TYPE, MEMBER) (size_t)(&((TYPE*)0)->MEMBER)
当我们将0强制类型转换为结构体类型,调用它的成员变量在取地址就能拿到它对应的偏移量。
而是否强制类型转换,其实影响不是很大,但为了更好的输出还是加上的好。
- 既然讲到了这里,我们在上面的也看到了在结构体中指针和数组似乎并无区别,但在我查阅资料和前辈所写的博客后,却并非如此(博客链接放到最开始的位置)。
3.2结构体指针和数组
当我们定义如下两个结构体:
//结构体1
struct Student
{int age;char phone[10];
};
int main(){struct Student* s1= (struct Student*)malloc (sizeof (struct line));s1->length = 10;memset(thisline->contents, 'a', 10);return 0;
}
//结构体2
struct Student
{int age;char* phone;
};
int main(){struct Student* s2 = (struct Student*)malloc (sizeof (struct line));s2->phone = (char*) malloc( sizeof(char) * 10);s1->length = 10;memset(thisline->contents, 'a', 10);return 0;
}
我们使用gbd调试后,可以清楚的看到它的内存的变化。
先来看一下结构体1,age占用4个字节的内存,phone[]占用10个字节的内存,一共占用14个字节的内存。
(这里的内存代码可能出现错误,但对于了解这方面知识不会产生影响)
(gdb) x /14b s1
0x601010: 10 0 0 0 97 97 97 97
0x601018: 97 97 97 97 97 97
前4个字节的内存是存放成员变量age的,后10个字节是存放phone[]的。
在看一下结构体2,与1相同,age占用4个字节的内存,指针phone开辟了10个字节。
(gdb) x /16b s2
0x601010: 1 0 0 0 32 16 96 0
(gdb) x /10b this->phone
0x601020: 97 97 97 97 97 97 97 97
0x601028: 97 97
我们可以清晰的看到,共占用了3行内存。
- 第一行前4个字节是int age。- 第一行后四个字节存放的是指针phone所存放内容的地址。- 它的地址是0x20 0x10 0x60,按小端存储,地址为0x601020- 第2和3行是指针phone所指向的内容。
- 这里我们就很清楚的看到了结构体中指针和数组的区别。
【C语言】深度探索offsetof,解析结构体的成员数组和指针相关推荐
- 【VS开发】【编程开发】【C/C++开发】结构体中的数组与指针的内存分配情况说明...
[VS开发][编程开发][C/C++开发]结构体中的数组与指针的内存分配情况说明 标签:[VS开发] [编程开发] 主要是疑惑在结构体定义的数组的内存空间与指针动态分配的内存空间,在地址上连续性.以及 ...
- c结构体里的数组与指针
/* 訪问成员数组名事实上得到的是数组的相对地址.而訪问成员指针事实上是相对地址里的内容 */ struct buf_str {int length;char buf[0]; };struct foo ...
- Go 语言快速入门指南:结构体
Go语言中没有"类"的概念,也不支持"类"的继承等面向对象的概念. Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. 1. 类型别名和自 ...
- Go语言全栈开发:结构体
文章目录 类型别名和自定义类型 结构体 练习题 Go语言中没有"类"的概念,也不支持"类"的继承等面向对象的概念.Go语言中通过结构体的内嵌再配合接口比面向对象 ...
- GO语言编程基础-复合类型结构体
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 1 结构体类型 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性.显然 ...
- C语言 匿名联合体和匿名结构体
C语言 匿名联合体和匿名结构体 匿名联合体和匿名结构体顾名思义,就是没有名字的联合体和结构体,没有名字哪怎么用?拿来用? 在嵌入式数据通信里面,大部分都是使用联合体,将有实际意义的结构体和字节数组互相 ...
- C语言中函数如何返回结构体?
//#include "stdafx.h"//If the vc++6.0, with this line. #include "stdio.h" #inclu ...
- c语言 typeof 结构体,Go语言通过反射获取结构体的成员类型
任意值通过 reflect.TypeOf() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的 NumField() 和 Field() 方法获得结构体成员的 ...
- c语言record的作用,C语言基础 record 2-指针,结构体,链表,文件的输入输出
指针: 1.指针的概念: 内存区的每一个字节有一个编号,这就是 '地址',由于通过地址能找到所需的变量单元,而地址指向该变量单元,所以将地址形象化称为 '指针'.C/C++中是可多继承的指针,而Jav ...
最新文章
- (备忘)Java数据类型中String、Integer、int相互间的转换
- selenium多个窗口
- 用户人品预测大赛--getmax队--竞赛分享
- FoxMail的25号端口被占用问题
- SCOUNIX第十二讲:使用TCP/IP
- 八年级计算机教学论文,初二数学教学论文范文
- css word-wrap_CSS中分词“ break-all”和“ break-word”的值之间的差异
- 罗永浩:6亿债务,已还了4个亿;Python 3.9指日可待|极客头条
- 禁止cmd dos 窗口被关闭
- kettle作业定时如何在开源调度工具taskctl里实现
- python培训 首选马哥教育
- android 天气类应用,一周天气预报!7款另类Android天气应用
- android应用商店完整版源码
- 一个小型VC项目的开发
- ArcMAP 添加注记与编辑
- 史上最详细:word文档怎么在第三页插入页码,只要7步
- 【常垒·投资】芯率智能完成A轮融资
- 清华张敏教授:个性化推荐研究进展(可解释性、鲁棒性和公平性)
- 【cocos2d-js】监听双击
- A. apache的安装