C语言结构体及其成员地址的互算

来源:互联网 发布:淘宝手机宝贝尺寸 编辑:程序博客网 时间:2024/05/16 05:38

http://blog.chinaunix.net/uid-20109769-id-261458.html

http://blog.csdn.net/npy_lp/article/details/7010752
#include <stdio.h>#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({                      \        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \        (type *)( (char *)__mptr - offsetof(type,member) );})#define container(ptr, type, member) (type *)( (char *)ptr - offsetof(type,member) )struct tag_test {  int i;  char s[32];  void *p;};#define print_addr(var) printf(" "#var": %p\n", var)int main(void) {    struct tag_test tstr;    struct tag_test *ca = container_of(&(tstr.p), struct tag_test, p);    struct tag_test *ca2 = container(&(tstr.p), struct tag_test, p);    int i_addr_i = offsetof( struct tag_test , i );    int i_addr_s = offsetof( struct tag_test , s );    int i_addr_p = offsetof( struct tag_test , p );    print_addr(&tstr);    print_addr(ca);    print_addr(ca2);    printf( " i_addr_i: %d\n", i_addr_i );    printf( " i_addr_s: %d\n", i_addr_s );    printf( " i_addr_p: %d\n", i_addr_p );    return 0;}
结果:
 &tstr: 0xbfae0914
 ca: 0xbfae0914
 ca2: 0xbfae0914
 i_addr_i: 0
 i_addr_s: 4
 i_addr_p: 36

说明:offsetof是求MEMBER在TYPE结构体中的偏移量,container_of是给定结构体某个成员的地址反推其结构体地址。两个宏实现的方法都很直观,无需多解释。倒是有一点:container_of是linux/kernel.h中定义的,其中用到的typeof操作符和语句表达式(即“({ statements })”这种写法)都是gcc对C的扩展,不是标准C。这两个feature的详细解释作为附录贴在本文末尾。我在程序里照猫画虎写了一个container宏,运行结果证明它和container_of功能一样,而且没有用到任何非标准C的东西。当然了,写Linux内核的人都是不知道比我牛多少倍的牛人,他们那样写一定是有他们的道理,只是我目前的水平还理解不了。先这么放着吧,等什么时候顿悟了再补上这一段。
其实它的语法很简单,只是一些指针的灵活应用,它分两步:

    第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。

    第二步,用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

其中代码难以理解的地方就是它灵活地运用了0地址。如果觉得&( (struct test_struct *)0 )->ch这样的代码不好理解,那么我们可以假设在0地址分配了一个结构体变量struct test_struct a,然后定义结构体指针变量p并指向a(struct test_struct *p = &a),如此我们就可以通过&p->ch获得成员ch的地址。由于a的首地址为0x0,所以成员ch的首地址为0x4。
最后通过强制类型转换(size_t)把一个地址值转换为一个整数。

typeof:
Another way to refer to the type of an expression is with typeof. The syntax of using of this keyword looks like sizeof, but the construct acts semantically like a type name defined with typedef.
There are two ways of writing the argument to typeof: with an expression or with a type. Here is an example with an expression:
     typeof (x[0](1))
This assumes that x is an array of pointers to functions; the type described is that of the values of the functions.
Here is an example with a typename as the argument:
     typeof (int *)
Here the type described is that of pointers to int.
A typeof-construct can be used anywhere a typedef name could be used. For example, you can use it in a declaration, in a cast, or inside of sizeof or typeof.
typeof is often useful in conjunction with the statements-within-expressions feature. Here is how the two together can be used to define a safe “maximum” macro that operates on any arithmetic type and evaluates each of its arguments exactly once:
     #define max(a,b) \
       ({ typeof (a) _a = (a); \
           typeof (b) _b = (b); \
         _a > _b ? _a : _b; })
Some more examples of the use of typeof:
This declares y with the type of what x points to.
          typeof (*x) y;
This declares y as an array of such values.
          typeof (*x) y[4];

Statements and Declarations in Expressions
A compound statement enclosed in parentheses may appear as an expression in GNU C. This allows you to use loops, switches, and local variables within an expression.
Recall that a compound statement is a sequence of statements surrounded by braces; in this construct, parentheses go around the braces. For example:
     ({ int y = foo (); int z;
        if (y > 0) z = y;
        else z = - y;
        z; })
is a valid (though slightly more complex than necessary) expression for the absolute value of foo ().
The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. (If you use some other kind of statement last within the braces, the construct has type void, and thus effectively no value.)
This feature is especially useful in making macro definitions “safe” (so that they evaluate each operand exactly once). For example, the “maximum” function is commonly defined as a macro in standard C as follows:
     #define max(a,b) ((a) > (b) ? (a) : (b))
But this definition computes either a or b twice, with bad results if the operand has side effects. In GNU C, if you know the type of the operands (here taken as int), you can define the macro safely as follows:
     #define maxint(a,b) \
       ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
0 0
原创粉丝点击