C语言 泛型链表 如何计算(结构体中各元素)相对内存地址?(字节对齐,结构体对齐)offsetof()函数 & ( (struct X*)0 ) -> Y)语法

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言 泛型链表 如何计算(结构体中各元素)相对内存地址?(字节对齐,结构体对齐)offsetof()函数 & ( (struct X*)0 ) -> Y)语法相关的知识,希望对你有一定的参考价值。

示例:

typedef struct  _user {
    char name[20];
    char sex[20];
    int age;
    struct list_head mylist;//自定义结构体里保存双向循环链表头结点
}user;

如果知道mylist的地址,如何获取user的地址呢?

可以这样:

u = (user*)((int)pos1 - sizeof(user) + sizeof((((user*)0)->mylist)));

使用mylist的地址pos1,减去user与mylist的容量大小之差,再将结果转换回user*类型,最后得到的就是user u的地址

注意:对指针直接加减,是相对于这个指针它自身的类型的,要把指针地址转换成实际数值再相加减

(user*)0用法可参考:C语言如何获取结构体中指定元素的大小?sizeof ( (X*)0 ) -> Y)

ffffff

但是不得不考虑到可能存在对齐的情况,就是空间与实际存储的数据大小不一致的情况,可见:C语言 如何计算结构体的大小

比如这里就发生了地址对齐:
在这里插入图片描述
char sex之后age之前空出了两个字节的存储空间,以满足后面int类型大小的倍数,但是因为在mylist之后就没发生过字节对齐的情况,(mylist存放的是prev* 和next*两个指针),所以对结果不影响

但是如果mylist之后还存在特殊情况,用上面的方法还能计算出mylist和user真实的地址差吗??

我们再来做个小测试:

&((user*)0)->mylist;
&(((user*)0)->mylist);
(int)&((user*)0)->mylist;

在这里插入图片描述
这个语法,实际上就是offsetof函数,但是我用不了这个函数不知道咋回事,直接把它源码弄来!
在这里插入图片描述
可以看到,->是比&优先级高的,所以不用加括号()

offsetof()函数是啥,可以看这:C语言中 offsetof 的使用

这个函数可以求得结构体中元素(头)相对于结构体(头)的地址差!

所以我们想要通过元素(头)地址求结构体(头)地址,只要这样即可:

u = (user*)((int)pos1 - (int)&((user*)0)->mylist);

是不是又比我们开头那会用的又简单了很多???!!!

参考文章:C语言如何获取结构体中指定元素的大小?sizeof ( (X*)0 ) -> Y)

完整泛型链表代码:by jianggong

main.c不是main.cpp!!

#include <stdio.h>
struct list_head {
    struct list_head* next;
    struct list_head* prev;
};


#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

//根据structd的成员获取struct的地址
#define container_of(ptr, type, member) ((type *)(((char *)ptr) - (int)(&(((type*)0)->member))))


//链表遍历

#define list_for_each(pos, head) \\
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_prev(pos, head) \\
    for (pos = (head)->prev; pos != (head); pos = pos->prev)





#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \\
    struct list_head name = LIST_HEAD_INIT(name)

static inline void __list_add(struct list_head* new_,
    struct list_head* prev,
    struct list_head* next)
{
    next->prev = new_;
    new_->next = next;
    new_->prev = prev;
    prev->next = new_;
}


static inline void list_add(struct list_head* new_, struct list_head* head)
{
    __list_add(new_, head, head->next);
}

static inline void list_add_tail(struct list_head* new_, struct list_head* head)
{
    __list_add(new_, head->prev, head);
}


static inline void __list_del(struct list_head* prev, struct list_head* next)
{
    next->prev = prev;
    prev->next = next;
}

static inline void list_del(struct list_head* entry)
{
    __list_del(entry->prev, entry->next);
}

typedef struct  _user {
    char name[11];
    char sex[11];
    int age;
    struct list_head mylist;//自定义结构体里保存双向循环链表头结点
}user;

typedef struct  _records {
    char name[20];
    int type;
    struct list_head mylist;//自定义结构体里保存双向循环链表头结点
}records;


int main()
{
    //LIST_HEAD(header_task);
    //初始化头结点
    struct list_head header_task1 = { &(header_task1), &(header_task1) };
    struct list_head header_task2 = { &(header_task2), &(header_task2) };

    user u1 = { "张麻子", "男", 40 };
    user u2 = { "牛魔王", "男", 36 };
    user u3 = { "蜘蛛精", "女", 28 };

    records r1 = {"张麻子", 1};
    records r2 = {"蜘蛛精", 2};
    records r3 = {"牛魔王", 1};

    //新建
    struct list_head* pos1;
    struct list_head* pos2;
    user* u;
    records* r;

    //头插法
    list_add(&u1.mylist, &header_task1);
    list_add(&u2.mylist, &header_task1);
    list_add(&u3.mylist, &header_task1);

    list_add(&r1.mylist, &header_task2);
    list_add(&r2.mylist, &header_task2);
    list_add(&r3.mylist, &header_task2);


    //list_for_each(pos1, &header_task1) {
    //    u = container_of(pos1, user, mylist);
    //    printf("data is %d\\n", u->age);
    //}
    for (pos1 = (&header_task1)->next; pos1 != (&header_task1); pos1 = pos1->next)
    {
        //u = container_of(pos1, user, mylist);
        //u = ((user*)(((char*)pos1) - (int)(&(((user*)0)->mylist))));
        //u = (user*)((int)pos1 - sizeof(user) + sizeof((((user*)0)->mylist)));
        //&((user*)0)->mylist;//->优先级高于&
        u = (user*)((int)pos1 - (int)&((user*)0)->mylist);
        printf("data is %d\\n", u->age);
    }
    printf("----------\\n");

    list_del(&u2.mylist);

    list_for_each(pos1, &header_task1) {
        u = container_of(pos1, user, mylist);
        printf("data is %d\\n", u->age);
    }

    printf("Hello World!\\n");
    return 0;
}

运行结果:

data is 28
data is 36
data is 40
----------
data is 28
data is 40
Hello World!

以上是关于C语言 泛型链表 如何计算(结构体中各元素)相对内存地址?(字节对齐,结构体对齐)offsetof()函数 & ( (struct X*)0 ) -> Y)语法的主要内容,如果未能解决你的问题,请参考以下文章

泛型链表结构

C语言,结构体中的数组怎么赋值,?

链表使用类和结构体的区别?

C语言 | 链表概述

c语言中如何将按结构体中的某个元素大小,将结构体排序输出

go语言 从结构体中获取某个字段的值(反射+泛型)