大家好,又见面了,我是你们的朋友全栈君。
定义及功能:
#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头文件
代码语言:javascript复制<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_ */
代码语言:javascript复制
offsetof_test.c
代码语言:javascript复制</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 : %dn", size); data = malloc(size); if (data == NULL) { perror("malloc"); goto failed; } size = sizeof(data->age); printf("my_data_t age 's size: %dn", size); size = sizeof(data->fullname); printf("my_data_t fullname 's size: %dn", size); size = sizeof(data->sex); printf("my_data_t sex 's size: %dn", size); offset = offsetof(my_data_t, age); printf("age in my_data_t 's offset is : %dn", offset); offset = offsetof(my_data_t, fullname); printf("fullname in my_data_t 's offset is : %dn", offset); offset = offsetof(my_data_t, sex); printf("sex in my_data_t 's offset is : %dn", 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
运行结果:
函数print_offsetof实现中,主要使用了 offsetof宏定义来获取一个结构体中的各个成员相对于结构体首地址的偏移量
根据结构体定义,不难理解上述输出的结果。
其中age是结构体定义中的第一项,它相对于结构体首地址的偏移地址为0
fullname是结构体的第二项,它相对于结构体首地址的偏移量为 age类型占用的字节数,为4
以后一次类推。
利用已知地址和offsetof来转换一个结构体的首地址
offsetof_test.h
代码语言:javascript复制/*
* 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
代码语言:javascript复制/*
* 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 : %dn", size);
data = malloc(size);
if (data == NULL) {
perror("malloc");
goto failed;
}
data->age = 32;
memset(data->fullname.name, '/*
* 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 : %dn", size);
data = malloc(size);
if (data == NULL) {
perror("malloc");
goto failed;
}
data->age = 32;
memset(data->fullname.name, '