C语言offsetof用法以及其扩展用法

本文详细介绍了C语言中的offsetof宏的用法及其在GCC编译器中的实现原理,同时探讨了offsetof如何用于获取结构体成员的偏移量,并通过实例展示了如何使用container_of宏从成员变量获取结构体指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标题C语言offsetof用法以及其扩展用法

offsetof由于不是标准库的函数,所以得查一下,在stddef.h中,搜索一下编译器的这个头文件位置:
暴力一点,直接在根目录下搜索,find -name “stddef.h”,找到了这个

/usr/lib/gcc/i586-suse-linux/4.8/include/stddef.h

打开看下文件内容,找到offsetof

/* Offset of member MEMBER in a struct of type TYPE. */
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)

这是目前GCC编译器的定义宏,在传统实现依赖于编译器多指针不是很挑剔,把结构体当做0地址处理,然后获取成员变量的偏移,如下:

#define offsetof(st, m) ((size_t)&(((st *)0)->m))

这个定义在C11标准下是非法的,0表示空,这里对空指针进行解引用,显然是有问题的。Linux用户老实用gcc的实现方式就ok。
Gcc的头文件注释就是针对结构体,查询该结构体的某个成员变量相对结构体偏移量,就是该成员前面还有多少字节的其他成员存储的内容。写个代码测试一下:

#include <stdio.h>
#include <stddef.h>

typedef struct Test
{
    int a;
    int b;
    char c;
    short d;
    float e;
}test;

int main(int argc, char *argv[])
{
    printf("offset of b = %d\n", offsetof(test, b));
    printf("offset of c = %d\n", offsetof(test, c));
    printf("offset of d = %d\n", offsetof(test, d));
    printf("offset of e = %d\n", offsetof(test, e));

    return 0;
}

编译执行结果如下:
在这里插入图片描述
说到这里可能都觉得,这个有啥用,我都知道这个结构体的成员变量了,我自己算出来不就完事了?
其实在应用层的开发中好像比较少用到这个东西,但是在linux内核开发和驱动开发中比较常用这个东西来通过某个成员变量获取结构体的首地址,来实现一些其他的操作。比如Linux内核使用offsetof()来实现container_of()

#define container_of(ptr, type, member) ({ \
        	const typeof( ((type *)0)->member ) *__mptr = (ptr); \
            (type *)( (char *)__mptr - offsetof(type,member) );})

这个宏经常被用来通过结构体某个内嵌成员变量来获得该成员变量外包结构体的指针,好利用该结构体指针做其他用途。
举个栗子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>


#define container_of(ptr, type, member) ({	    \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})


typedef struct Test
{
    int a;
    int b;
    char c;
    short d;
    float e;
}test;

int main(int argc, char *argv[])
{
    /*
    printf("offset of b = %d\n", offsetof(test, b));
    printf("offset of c = %d\n", offsetof(test, c));
    printf("offset of d = %d\n", offsetof(test, d));
    printf("offset of e = %d\n", offsetof(test, e));
    */
    test *my_test = NULL;
    my_test = (test *)malloc(sizeof(test));
    if(!my_test)
    {
        return -1;
    }
    memset(my_test, 0, sizeof(test));
    
    my_test->a = 1;
    my_test->b = 2;
    my_test->c = 3;
    my_test->d = 4;
    my_test->e = 5;

    int *ptr = &my_test->b;
    test *ret = container_of(ptr, test, b);
    
    printf("ret->b = %d\n", ret->b);
	
	free(my_test);
	my_test = NULL;
    return 0;
}

编译运行:

root@LYX:~/share/test/c_offect# gcc offset.c 
root@LYX:~/share/test/c_offect# ./a.out 
ret->b = 2

细心的同学可能会发现,我的运行环境变了。没关系,这个一部分是在公司的休息时间写的,一部分是在家的pc上写的,所以会有环境的细微变化,不影响。
通过上面这个例子,是不是就可以解决只知道某个结构体的成员变量,然后通过这个成员变量找到该结构体的指针,这一用法其实在内核中很常见,如一些链表的操作。
说到这里我突然想到一个问题:一个单链表,先有一个指针指向其中一个节点,怎么获得他的上一个节点,或者有个指针指向该节点的某个成员变量,求上一个节点,是不是可以用这种方法去做,这里不展开去讲了,改天有时间写个代码出来实操一遍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lyx_wmt

能白嫖就绝不打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值