1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 【C语言】深度探索offsetof 解析结构体的成员数组和指针

【C语言】深度探索offsetof 解析结构体的成员数组和指针

时间:2019-11-10 09:15:44

相关推荐

【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;

如图是在VS中调试的结果,我们可以看到各成员变量的地址。

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结构体指针和数组

当我们定义如下两个结构体:

//结构体1struct 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;}

//结构体2struct 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 s10x601010: 100 0 0 979797970x601018: 979797979797

前4个字节的内存是存放成员变量age的,后10个字节是存放phone[]的。

在看一下结构体2,与1相同,age占用4个字节的内存,指针phone开辟了10个字节。

(gdb) x /16b s20x601010: 1 0 0 0 3216960(gdb) x /10b this->phone0x601020: 97979797979797970x601028: 9797

我们可以清晰的看到,共占用了3行内存。

- 第一行前4个字节是int age。- 第一行后四个字节存放的是指针phone所存放内容的地址。- 它的地址是0x20 0x10 0x60,按小端存储,地址为0x601020- 第2和3行是指针phone所指向的内容。

这里我们就很清楚的看到了结构体中指针和数组的区别。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。