• CONTAINING_RECORD


     CONTAINING_RECORD 这样的一个宏,我看了它的定义,如下:
    #define CONTAINING_RECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field)))

    class A
    {
          char c;
          int a;
          short b;
    }

    int a = 100;
    int *pInt = &a;
    比如,我调用了 CONTAINING_RECORD(pInt,A,a);
    完全展开来后如下:
    (A*)((char*)pInt - (unsigned long)(&((A*)0)->a))

    为什么要用这个宏:
           这个宏所做的操作其实就是把pInt与A结构中的相应的类型值进行一个位置配对。上面可以看到int a定义在第二个数据中,可以想象,如果我们有a的地址,然后直接把a转化成A*的话,很明显,a的地址就变成了A*的首地址了,但问题是A的第一个元素是char型的,这样的话,pInt就无法对齐上结构中的a元素的位置了。所以要进行一个偏移量操作.

    下面,我们下解析一下:
            首先,红色的部分很容易理解,我们知道,如果有一个int *a;的指针,我们a - 1,其实相当于a - sizeof(int),相于于把指针向右移了4个位置,把一个指针转化成一个char*型,这样,进行四则运算时就会按照我们正常的操作,(char*)(a - 1)就只是把指针移动了一个位置。
            然后,看下紫色的部分,首先,要明白,对0指针的取值操作并不会出错,只是不确定这个值返回的是什么值,当然,如果我们对这个值进行修改,这是很危险的。这里用的0位置指针是很特别的,相对于0的位置,0指针对->a的操作,返回的数值取地址值后再转化成unsigned long值,其实得到的就是a相对于结构体A来说,偏移了多少个位置。0是起始地址,那么对于一个->操作,简单来理解,其实相当于0(结构体起始地址)+ sizeof(a前面的数据),当然,这里要考虑字节对齐的问题不过,编译器还是会帮我们把这些都完成。
           最后,我们知道了pInt的结构体的首地址,知道了a的偏移地址,那么我们把pInt的地址值-偏移量,相当于把pInt倒退了偏移量个地址值,然后,我们再转换甩A*的话,相当于A*的起始地址已经是pInt的前面偏移量个地址,也就是a最前面的一个元素的地址值,对于A来说就是char c的地址,这样,我们就得到了正确的起始地址,然后再转换成(A*)的话,我们的pInt就能和A*的a的地址对应上了.
  • 相关阅读:
    迭代
    UIViewController生命周期控制
    JPA相关注解
    正則表達式截取字符串两字符间的内容
    HDU 1789 Doing Homework again
    《从零開始学Swift》学习笔记(Day48)——类型检查与转换
    POJ 3280 Cheapest Palindrome(区间DP)
    JavaScript高级特性之原型
    http协议
    编程算法
  • 原文地址:https://www.cnblogs.com/mokliu/p/2652723.html
Copyright © 2020-2023  润新知