利用结构体偏移量的高级C应用
Linux 存在两种结构体偏移量的函数
Linux 内核层: container_of(ptr,type,member) —> 宏函数
Linux 应用层: size_t offsetof(type, member); —> 函数
Linux 结构体利用偏移量,进行指针操作,从而取得指针值
- 结构体定义
struct space
{
unsigned int episode;
unsigned long off;
unsigned char name[128];
unsigned long allocated;
unsigned long used;
};
struct episode_head
{
unsigned int start;
unsigned int num;
struct space space;
unsigned int end;
};
- 主函数
int main(int argc, char const *argv[])
{
int off = 0;
unsigned int block = 0;
struct episode_head *head = NULL;
head = (struct episode_head*)calloc(1, sizeof(struct episode_head));
if(NULL == head)
{
fprintf(stderr, "ralloc for head failed\r\n" );
return -1;
}
head->start = 1;
head->num = 2;
head->space.episode = 3;
head->space.off = 4;
snprintf(head->space.name, sizeof(head->space.name), "hello world");
head->space.allocated = 6;
head->space.used = 7;
head->end = 8;
unsigned int base_addr = (unsigned int)head;
unsigned int start_addr = (unsigned int)(&head->start);
unsigned int num_addr = (unsigned int)(&head->num);
unsigned int episode_addr = (unsigned int)(&head->space.episode);
unsigned int off_addr = (unsigned int)(&head->space.off);
unsigned int name_addr = (unsigned int)(&head->space.name);
unsigned int allocated_addr = (unsigned int)(&head->space.allocated);
unsigned int used_addr = (unsigned int)(&head->space.used);
unsigned int end_addr = (unsigned int)(&head->end);
unsigned int start_addr_offset = start_addr-base_addr;
unsigned int num_addr_offset = num_addr-base_addr;
unsigned int episode_addr_offset = episode_addr-base_addr;
unsigned int off_addr_offset = off_addr-base_addr;
unsigned int name_addr_offset = name_addr-base_addr;
unsigned int allocated_addr_offset = allocated_addr-base_addr;
unsigned int used_addr_offset = used_addr-base_addr;
unsigned int end_addr_offset = end_addr-base_addr;
printf("\r\n======================================\r\n\r\n");
printf("base space: %p\r\n", base_addr);
printf(" start: %p,\t offset: %u\r\n", start_addr, start_addr_offset);
printf(" num: %p,\t offset: %u\r\n", num_addr, num_addr_offset);
printf(" episode: %p,\t offset: %u\r\n", episode_addr, episode_addr_offset);
printf(" off: %p,\t offset: %u\r\n", off_addr, off_addr_offset);
printf(" name: %p,\t offset: %u\r\n", name_addr, name_addr_offset);
printf(" allocated: %p,\t offset: %u\r\n", allocated_addr, allocated_addr_offset);
printf(" used size: %p,\t offset: %u\r\n", used_addr, used_addr_offset);
printf(" end: %p,\t offset: %u\r\n", end_addr, end_addr_offset);
printf("\r\n======================================\r\n\r\n");
printf(" start: %d\r\n", *(int*)((char*)head + start_addr_offset));
printf(" num: %d\r\n", *(int*)((char*)head + num_addr_offset));
printf(" episode: %d\r\n", *(int*)((char*)head + episode_addr_offset));
printf(" off: %d\r\n", *(int*)((char*)head + off_addr_offset));
printf(" name: %s\r\n", (char*)((char*)head + name_addr_offset));
printf("allocated: %d\r\n", *(int*)((char*)head + allocated_addr_offset));
printf("used size: %d\r\n", *(int*)((char*)head + used_addr_offset));
printf(" end: %d\r\n", *(int*)((char*)head + end_addr_offset));
printf("\r\n======================================\r\n\r\n");
return 0;
}
- 结果
container_of 和 offsetof 结合起来,可以简化上述的操作
- container_of 属于 Linux 内核的宏函数,在应用层上进行调用,需要重写该函数:
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char*)__mptr - offsetof(type, member)); \
})
// offsetof 可以不用重写,因为应用层上有该函数
- container_of 直接返回结构体的地址,对应结构体的偏移为0,相当于返回的是结构体的指针
struct episode_head *epi_head = container_of(&head->end,struct episode_head,end);
printf("container_of(&head->end,struct episode_head,end):\r\n\r\n");
printf(" start: %d\r\n", epi_head->start);
printf(" num: %d\r\n", epi_head->num);
printf(" episode: %d\r\n", epi_head->space.episode);
printf(" off: %d\r\n", epi_head->space.off);
printf(" name: %s\r\n", epi_head->space.name);
printf("allocated: %d\r\n", epi_head->space.allocated);
printf("used size: %d\r\n", epi_head->space.used);
printf(" end: %d\r\n", epi_head->end);
结果如下:
- 结构体里定义的结构体变量,也可以使用 container_of 进行返回结构体地址,只是返回的结构体指针的类型为内部结构体变量的类型指针
struct space *ptr = container_of((void*)head->space.name,struct space,name);
printf("container_of((void*)head->space.name,struct space,name):\r\n\r\n");
printf(" episode: %d\r\n", ptr->episode);
printf(" off: %d\r\n", ptr->off);
printf(" name: %s\r\n", ptr->name);
printf("allocated: %d\r\n", ptr->allocated);
printf("used size: %d\r\n", ptr->used);
结果如下: