今天在geeksforgeeks(http://www.geeksforgeeks.org/the-offsetof-macro/)上面发现了一个自定义的Marco,如下:
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
用来求一个Element 在 Type 中的偏移量。其代码如下:
#include <stdio.h>
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
typedef struct PodTag
{
int i;
double d;
char c;
} PodType;
int main()
{
printf("%d", OFFSETOF(PodType, c) );
getchar();
return 0;
}
抛开OFFSETOF这个函数不说,先来考虑sizeof(PodType)。 最开始我以为是4+8+1 = 13。
结果是 8+8+8 = 24。turns out 编译的时候,会把每一个变量自动补齐成最长的变量,也就是double。后来在这个帖子里找到了解答
原文如下:(http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member)
It's for alignment. Many processors can't access 2- and 4-byte quantities (e.g. ints and long ints) if they're crammed in every-which-way.
Suppose you have this structure:
struct {
char a[3];
short int b;
long int c;
char d[3];
};
Now, you might think that it ought to be possible to pack this structure into memory like this:
+-------+-------+-------+-------+
| a | b |
+-------+-------+-------+-------+
| b | c |
+-------+-------+-------+-------+
| c | d |
+-------+-------+-------+-------+
But it's much, much easier on the processor if the compiler arranges it like this:
+-------+-------+-------+
| a |
+-------+-------+-------+
| b |
+-------+-------+-------+-------+
| c |
+-------+-------+-------+-------+
| d |
+-------+-------+-------+
In the packed version, notice how it's at least a little bit hard for you and me to see how the b and c fields wrap around? In a nutshell, it's hard for the processor, too. Therefore, most compilers will pad the structure (as if with extra, invisible fields) like this:
+-------+-------+-------+-------+
| a | pad1 |
+-------+-------+-------+-------+
| b | pad2 |
+-------+-------+-------+-------+
| c |
+-------+-------+-------+-------+
| d | pad3 |
+-------+-------+-------+-------+
这个解答太简单易懂了,我直接复制过来了。
第二,怎么理解OFFSETOF这个marco
((size_t)&(((TYPE *)0)->ELEMENT))
首先,看最前面,我们返回的是一个(size_t)类型,也就是unsigned int,其次我们是把(((TYPE *)0)->ELEMENT)这个东西的地址,转换成了unsigned int然后再返回的。 然后对于(((TYPE *)0)->ELEMENT)这个来说,首先我们看到了一个((TYPE *)0),这个也是一个类型转换,首先它是把一个数字0转换成了一个TYPE的指针。于是这个指针的地址就是0,并且它指向了一个TYPE的结构体,然后通过->指向我所求的变量。这样,这个变量的地址是多少,它的偏移就是多少,因为OFFSET-0 == OFFSET。
并且通过这个来求OFFSET,我们学会一个小技巧,可以通过(TYPE*)0来获得一个地址为0,指向TYPE的指针。