利用结构体偏移量的高级C应用

11 篇文章 0 订阅

利用结构体偏移量的高级C应用

Linux 存在两种结构体偏移量的函数

Linux 内核层: container_of(ptr,type,member) —> 宏函数
Linux 应用层: size_t offsetof(type, member); —> 函数

Linux 结构体利用偏移量,进行指针操作,从而取得指针值

  1. 结构体定义
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;
};
  1. 主函数
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;
}
  1. 结果
    在这里插入图片描述

container_of 和 offsetof 结合起来,可以简化上述的操作

  1. container_of 属于 Linux 内核的宏函数,在应用层上进行调用,需要重写该函数:
#define container_of(ptr, type, member) ({                \
	const typeof(((type *)0)->member) *__mptr = (ptr);    \
	(type *)((char*)__mptr - offsetof(type, member));     \
})

// offsetof 可以不用重写,因为应用层上有该函数
  1. 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);

结果如下:
在这里插入图片描述

  1. 结构体里定义的结构体变量,也可以使用 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);

结果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值