c语言offsetof_c语言宏定义比较大小

c语言offsetof_c语言宏定义比较大小根据一个已经分配空间的结构体指针a中的某个成员b的地址,来获取该结构体指针地址

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

定义及功能:

#include <stddef.h>
#define offsetof(type, member) (size_t)&(((type*)0)->member)

获取类型type中的成员member,相对于type类型的偏移量

将地址0强制转换为type类型的指针,从而定位到member在结构体中偏移位置。

编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。

一个经典的使用场景

使用offsetof宏,根据已知的一个已经分配空间的结构体对象指针a中的某个成员b的地址,来获取该结构体指针对象a地址。

而结构体a可能是一个比较大的对象,而结构体a的成员b是一个比较小的对象,这个小对象可以在一些数据结构中(比如红黑树中被保存),这样可以根据b反着获取a,从而继续在后续代码中使用a以及a的成员做后续处理。(nginx是如何实现的,见本文最后


代码简要说明:

1、存在一个较大的结构体a,demo中命名为 my_data_t。实际工程中,这个结构体可以是一个非常大的结构体对象,比如nginx中的ngx_event_t

2、存在一个较小的结构体b,demo中命名为my_str_t。实际工程中,这个结构体可以是一个较小的结构体对象。比如nginx中的ngx_rbtree_node_t

3、为结构体a分配空间,维护结构体a中的成员b的地址。在适当的时候,根据b的地址,以及使用offsetof获取b在a中的偏移量,从而获得a的地址。为后续的程序所用。


上代码:

offsetof_test.h头文件

<pre name="code" class="cpp">/*
 * offsetof_test.h
 *
 *  Created on: Jul 9, 2014
 *      Author: lingyun
 */

#ifndef OFFSETOF_TEST_H_
#define OFFSETOF_TEST_H_

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

#define NAME_SIZE 128

typedef struct my_data_s my_data_t;
typedef struct my_str_s my_str_t;

struct my_str_s {
	size_t    len;
	char      name[NAME_SIZE];
};

struct my_data_s {
	int age;
	my_str_t fullname;
	char sex;
};

void print_offset();

#endif /* OFFSETOF_TEST_H_ */


offsetof_test.c

</pre><pre name="code" class="cpp">/* * offsetof_test.c * *  Created on: Jul 9, 2014 *      Author: lingyun */#include "offsetof_test.h"const char       *name = "lingyun, liyanhua, lingyutian a family";static void free_data(my_data_t *data);staticvoid free_data(my_data_t *data) {	if(data != NULL) {		free(data);		data = NULL;	}}voidprint_offset() {	my_data_t        *data;	size_t            size;	size_t            offset;	size_t            name_size;	name_size = strlen(name);	size = sizeof(my_data_t);	printf("my_data_t size is : %d\n", size);	data = malloc(size);	if (data == NULL) {		perror("malloc");		goto failed;	}	size = sizeof(data->age);	printf("my_data_t age 's size: %d\n", size);	size = sizeof(data->fullname);	printf("my_data_t fullname 's size: %d\n", size);	size = sizeof(data->sex);	printf("my_data_t sex 's size: %d\n", size);	offset = offsetof(my_data_t, age);	printf("age in my_data_t 's offset is : %d\n", offset);	offset = offsetof(my_data_t, fullname);	printf("fullname in my_data_t 's offset is : %d\n", offset);	offset = offsetof(my_data_t, sex);	printf("sex in my_data_t 's offset is : %d\n", offset);failed:	free_data(data);}int main() {	print_offset();	return 0;}


编译:

gcc -c offsetof_test.c -o offsetof_test.o

gcc -o main offsetof_test.o

./main


运行结果:
c语言offsetof_c语言宏定义比较大小


函数print_offsetof实现中,主要使用了 offsetof宏定义来获取一个结构体中的各个成员相对于结构体首地址的偏移量

根据结构体定义,不难理解上述输出的结果。

其中age是结构体定义中的第一项,它相对于结构体首地址的偏移地址为0

fullname是结构体的第二项,它相对于结构体首地址的偏移量为 age类型占用的字节数,为4

以后一次类推。


利用已知地址和offsetof来转换一个结构体的首地址

offsetof_test.h

/*
 * offsetof_test.h
 *
 *  Created on: Jul 9, 2014
 *      Author: lingyun
 */

#ifndef OFFSETOF_TEST_H_
#define OFFSETOF_TEST_H_

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

#define NAME_SIZE 128

typedef struct my_data_s my_data_t;
typedef struct my_str_s my_str_t;

struct my_str_s {
	size_t    len;
	char      name[NAME_SIZE];
};

struct my_data_s {
	int age;
	my_str_t fullname;
	char sex;
};

void use_offsetof();

#endif /* OFFSETOF_TEST_H_ */

offsetof_test.c

/*
 * offsetof_test.c
 *
 *  Created on: Jul 9, 2014
 *      Author: lingyun
 */

#include "offsetof_test.h"

const char       *name = "lingyun, liyanhua, lingyutian a family";

static void free_data(my_data_t *data);

static
void free_data(my_data_t *data) {
	if(data != NULL) {
		free(data);
		data = NULL;
	}
}

void
use_offsetof() {

	my_data_t        *data;
	size_t            size;
	size_t            offset;
	size_t            name_size;
	my_str_t         *fullname_ptr = NULL;
	my_data_t        *data_ptr = NULL;

	name_size = strlen(name);

	size = sizeof(my_data_t);
	printf("my_data_t size is : %d\n", size);
	data = malloc(size);
	if (data == NULL) {
		perror("malloc");
		goto failed;
	}

	data->age = 32;

	memset(data->fullname.name, '\0', NAME_SIZE);
	strncpy(data->fullname.name, name, name_size);
	data->fullname.len = name_size;

	data->sex = 'm';

	printf("my_data_t 's instance address: %p\n", data);
	printf("my_data_t 's fullname 's address: %p\n", &data->fullname);

	printf("char * 's size: %d\n", sizeof(char *));
	printf("int * 's size: %d\n", sizeof(int *));


	fullname_ptr = &data->fullname;

	printf("data->fullname 's address: %p\n", fullname_ptr);
	printf("data->fullname 's address(convert to char *): %p\n", (char *)fullname_ptr);
	printf("data->fullname 's address(convert to int *): %p\n", (int *)fullname_ptr);

//	printf("data->fullname 's address(convert to char * and offset 4): %p\n", ((char *)fullname_ptr) - 4);
//
//	printf("data->fullname 's address(convert to int * and offset 1): %p\n", ((int *)fullname_ptr) - 1);
//	printf("data->fullname 's address(convert to int * and offset 2): %p\n", ((int *)fullname_ptr) - 2);
//	printf("data->fullname 's address(convert to int * and offset 3): %p\n", ((int *)fullname_ptr) - 3);
//	printf("data->fullname 's address(convert to int * and offset 4): %p\n", ((int *)fullname_ptr) - 4);

	offset = offsetof(my_data_t, fullname);
	printf("fullname in my_data_t 's offset is : %d\n", offset);

//	data_ptr = (my_data_t *)((int *)fullname_ptr - offsetof(my_data_t, fullname));
	data_ptr = (my_data_t *)((char *)fullname_ptr - offsetof(my_data_t, fullname));

	printf("data_ptr 's address: %p\n", data_ptr);

	printf("name from data_ptr: %s\n", data_ptr->fullname.name);
	printf("name length from data_ptr: %d\n", data_ptr->fullname.len);
	printf("age from data_ptr: %d\n", data_ptr->age);
	printf("sex from data_ptr: %c\n", data_ptr->sex);

failed:
	free_data(data);
}

int main() {
	use_offsetof();	
	return 0;
}

编译:

gcc -c offsetof_test.c -o offsetof_test.o

gcc -o main offsetof_test.o

./main


运行结果:

c语言offsetof_c语言宏定义比较大小

总结:

1、由于知道偏移的字节数为4,所以将fullname_ptr转换为(char *),这样在减去偏移量时,减4逻辑才正确。

如果将fullname_ptr转换为(int *)类型,这样再减4的时候,会在0x8fde00c的基础上,减掉16个字节。一个int占用4个字节,偏移4代表偏移4个int,即16个字节


即如下代码的运行结果是不一样的:

	printf("data->fullname 's address(convert to char * and offset 4): %p\n", ((char *)fullname_ptr) - 4);
	printf("data->fullname 's address(convert to int * and offset 4): %p\n", ((int *)fullname_ptr) - 4);


2、my_data_t类型中的fullname成员的地址,可以被保存在公用数据结构中,比如hash或tree中作为节点。当遍历hash或tree获取到该节点后,根据上述转换思路,即可获取一个包含fullname的结构体对象指针,为后续处理提供数据。只在hash或tree中保留fullname的地址,是有显而易见的好处的。这样存储空间小,同时在需要fullname的父结构体(my_data_t)类型的指针时,可以很方便的获取。


nginx中是如何使用offsetof来工作的


nginx中采用这一思想,使用 ngx_event_t 和 ngx_rbtree_node_t 完成了定时器到期时,要执行事件时,根据ngx_rbtree_node_t地址来获取ngx_event_t对象指针的实现。


具体参见:

src/event/ngx_event_timer.c 中的如下代码


        if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0) {
            ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));

而ngx_event_t类型对象是如何将其结构体中的timer加入到红黑树中的,参见

src/event/ngx_event_timer.h


static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
	//这里省略了一些代码
	
    ngx_mutex_lock(ngx_event_timer_mutex);

    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);

    ngx_mutex_unlock(ngx_event_timer_mutex);

    ev->timer_set = 1;
}


注:该代码所属的nginx版本是1.4.2


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/172087.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • android之switch控件的用法

    在做一个蓝牙开关时候,用到了switch,记一下用法,其实跟Button是几乎一样的.布局中:<Switch android:id=”@+id/open” android:layout_width=”wrap_content” android:layout_height=”wrap_content” android…

    2022年3月10日
    40
  • ie浏览器activexobject_ie8 object.defineproperty

    ie浏览器activexobject_ie8 object.defineproperty切记:ActiveX是微软的东西,故而这玩意儿只有IE才支持!JavaScript中ActiveXObject对象是启用并返回Automation对象的引用,javaScript中利用ActiveXObject来创建FileSystemObject操作文件。一、功能实现核心:FileSystemObject对象要在javascript中实现文件操作功能,主要就是依

    2022年10月11日
    0
  • linux vi 替换命令_vi替换命令转移

    linux vi 替换命令_vi替换命令转移原文地址:http://www.cnblogs.com/afant/archive/2009/03/11/1408745.html:s/^.*$/\L&/100#将100行内的小写转换成大写vi/vim中可以使用:s命令来替换字符串。:s/vivian/sky/替换当前行第一个vivian为sky:s/vivian/sky/g替换当前行所有vi

    2022年9月22日
    1
  • 简单的关机程序(C语言)

    简单的关机程序(C语言)Windows系统自带一个名为Shutdown.exe的程序,可以用于关机操作(位置在Windows\System32下),一般情况下Windows系统的关机都可以通过调用程序shutdown.exe来实现的,同时该程序也可以用于终止正在计划中的关机操作。while循环实现:#include<stdio.h>#include<stdlib.h>intmain(){ charinput[20]={0}; system(“showdown-s-t60.

    2022年7月22日
    13
  • stm32 带通滤波器_带通滤波器详解_带通滤波器工作原理_带通滤波器原理图

    stm32 带通滤波器_带通滤波器详解_带通滤波器工作原理_带通滤波器原理图带通滤波器(band-passfilter)是一个国家允许使用特定频段的波通过发展同时进行屏蔽其他频段的设备。比如RLC振荡回路问题就是这样一个可以模拟带通滤波器。带通滤波器是一种滤波器,它可以在一定的频率范围内通过频率分量,但将其他范围内的频率分量衰减到非常低的水平,与带阻滤波器的概念形成对比。模拟带通滤波器的一个例子是电阻电感电容电路(RLC电路)。这些滤波器也可以通过将低通滤波器与高通…

    2022年5月2日
    29
  • phpstrom 2021.3激活码 3月最新注册码

    phpstrom 2021.3激活码 3月最新注册码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月15日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号