首页 > 代码库 > 结构体指针和结构体成员指针

结构体指针和结构体成员指针

在添加sqlite3的加解密功能函数时,由于网上资料针对的版本是旧版sqlite3源码,现在新版上做修改遇到了一个宏,该宏是旧版上的,它作用是由结构体成员指针pData找到它所属的结构体指针。下面转的文章详解了这种机制。

1.通过结构体成员指针寻找其所属结构体指针

转自http://blog.csdn.net/mycwq/article/details/9327743

宏CONTAINING_RECORD,可以直接根据结构体成员指针找到结构体指针。

我们看一下它的定义:

1 #define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))2 //  addr:  结构体中某个成员变量的地址3 //  type:  结构体的原型4 //  field: 结构体的某个成员(与前面相同)

为什么这样就能得到结构体指针?

这个得益于结构体在申请内存空间是一段连续的地址,我们可以通过其中某个成员变量的地址,减去该成员的偏移来得到该成员所在结构体的指针。 

那么为何(unsigned long)&((type*)0)->field))能得到某结构体成员的偏移?

这里可以假设某结构体指针地址是0000000,那么该成员的地址就是该成员对于其结构体的偏移。

小小:该宏应该是C++中的,但是原理很优雅。

2.C语言结构体成员指针偏移研究

转自http://m.oschina.net/blog/140463

问题1

我们先来定义一下需求:

已知结构体类型定义如下:

struct node_t{    char a;    int b;    int c;};

且结构体1Byte对齐

#pragma pack(1)

求:

结构体struct node_t中成员变量c的偏移。

注:这里的偏移量指的是相对于结构体起始位置的偏移量。

看到这个问题的时候,我相信不同的人脑中浮现的解决方法可能会有所差异,下面我们分析以下几种可能的解法:

方法1

如果你对c语言的库函数比较熟悉的话,那么你第一个想到的肯定是offsetof函数(其实只是个宏而已,先姑且这样叫着吧),我们man 3 offsetof查看函数原型如下:

 #include <stddef.h>       size_t offsetof(type, member);

应用上述的库函数,我们用一行代码就可以搞定:

  offsetof(struct node_t, c);

方法2

当我们对c语言的库函数不熟悉的时候,此时也不要着急,我们依然可以使用我们自己的方法来解决问题。

最直接的思路是:【结构体成员变量c的地址】 减去 【结构体起始地址】

我们先来定义一个结构体变量node:

struct node_t node; 

接着来计算成员变量c的偏移量:

(unsigned long)(&(node.c)) - (unsigned long)(&node)

 &(node.c)为结构体成员变量c的地址,并强制转化为unsigned long;

&node为结构体的起始地址,也强制转化为unsigned long;

最后我们将上述两值相减,得到成员变量c的偏移量。

方法3

(unsigned long)(&(((struct node_t *)0)->c))

上述的代码相对于方法2是不是更简洁了一些。

这里我们将上面的代码功能定义为一个宏,该宏的作用是用来计算某结构体内成员变量的偏移(后面的示例会使用该宏):

#define OFFSET_OF(type, member) (unsigned long)(&(((type *)0)->member))

使用上面的宏,就可以直接得到成员变量c在结构体struct node_t中的偏移为:

OFFSET_OF(struct node_t, c)

问题2

示例1一样,我们先定义需求如下:

已知结构体类型定义如下:

struct node_t{    char a;    int b;    int c;};

 

int *p_c,该指针指向struct node_t x的成员变量c

结构体1Byte对齐

#pragma pack(1)

求:

结构体x的成员变量b的值?

方法1

由于我们知道结构体是1Byte对齐的,所以这道题最简单的解法是:

*(int *)((unsigned long)p_c - sizeof(int))

上述代码很简单,成员变量c的地址减去sizeof(int)从而得到成员变量b的地址,然后再强制转换为int *,最后再取值最终得到成员变量b的值;

方法2

方法1的代码虽然简单,但扩展性不够好。我们希望通过p_c直接得到指向该结构体的指针p_node,然后通过p_node访问该结构体的任意成员变量了。

由此我们得到计算结构体起始地址p_node的思路为:

【成员变量c的地址p_c】减去【c在结构体中的偏移】

由示例1,我们得到结构体struct node_t中成员变量c的偏移为:

(unsigned long)&(((struct node_t *)0)->c)

所以我们得到结构体的起始地址指针p_node为:

(struct node_t *)((unsigned long)p_c - (unsigned long)(&((struct node_t *)0)->c))

我们也可以直接使用示例1中定义的OFFSET_OF宏,则上面的代码变为:

(struct node_t *)((unsigned long)p_c - OFFSET_OF(struct node_t, c))

最后我们就可以使用下面的代码来获取成员变量a,b的值:

p_node->ap_node->b

我们同样将上述代码的功能定义为如下宏:

#define STRUCT_ENTRY(ptr, type, member) (type *)((unsigned long)(ptr)-OFFSET_OF(type, member))

该宏的功能是通过结构体任意成员变量的指针来获得指向该结构体的指针。

结构体指针和结构体成员指针