如何通过结构体成员的地址获取结构体变量的地址?

  • C 语言的结构体可以将不同类型的对象聚合到一个对象中,在内存中,编译器按照成员列表顺序分别为每个结构体变量成员分配内存,但由于 C 的内存对齐机制以及不同机器间的差异,各个成员之间可能会有间隙,所以不能简单的通过成员类型所占的字长来推断其它成员或结构体对象的地址。

  • 如果要计算结构体中某成员相对于该结构体首地址的偏移量,一般第一个反应就是该成员的地址与结构体对象的首地址之间的字节数。
    这里写图片描述

typedef struct _tTask {
    tTaskStack * stack;
    uint32_t delayTicks;
    // 延时结点:通过delayNode就可以将tTask放置到延时队列中
    tNode delayNode;
    uint32_t prio;
    uint32_t state;
 }tTask;

// tinyOS链表的结点类型
typedef struct _tNode
{
    struct _tNode * preNode;
    struct _tNode * nextNode;
}tNode;

#define tNodeParent(node, parent, name) (parent *)((uint32_t)node - (uint32_t)&((parent *)0)->name)

tNode * node;

// 检查所有任务的delayTicks数,如果不0的话,减1。
for (node = tTaskDelayedList.headNode.nextNode; node != &(tTaskDelayedList.headNode); node = node->nextNode)
{
    tTask * task = tNodeParent(node, tTask, delayNode);
    if (--task->delayTicks == 0) 
    {
        // 将任务从延时队列中移除
        tTimeTaskWakeUp(task);

        // 将任务恢复到就绪状态
        tTaskSchedRdy(task);            
    }
}
  1. 一个parent代表结构体的类型,name代表结构体中的成员。
  2. ((parent*)0),把数字0强制转换成parent 结构体指针类型。这样 ((parent )0) 这个整体就相当于一个指针指向了 0 这个地址,不管 0 这个地址是否合法,是否真的有这么一个结构体对象,它都会把以 0 地址为首的一片连续内存当成一个结构体对象操作。
  3. ((parent*)0)->name,结构体指针((parent*)0)取结构体对象中name成员。因为这只是对内存操作,并没有写内存,虽然地址不合法也不会出现段错误。
  4. &((parent *)0)->name对name成员取地址。
  5. offset = (uint32_t)&((parent *)0)->name偏移量。因为这个parent类型结构体对象是从0地址开始的,故而offset就是成员name的偏移量。

知道成员偏移量,就很容易求结构体对象本身的地址。成员的地址减去偏移量就是是结构体对象的首地址!
((parent*)0)((uint32_t)node - (uint32_t)&((parent*)0)->name)结构体对象的首地址,即任务控制块。

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页