关于取struct成员的偏移

从朋友博客上看到有如下一个有趣的笔试题:

struct s{
int a;
int b;
char c[8];
void* d;
}mystruct;

1,问&((struct s*)0)->d出现设么结果?
2,这种类型的代码一般出现在什么情况下?

答案是:

116
2
。取结构内元素的偏移时用。

这种取结构内元素的偏移的写法是第一次看到,非常令人惊奇。为了更好地理解它,下面仔细地看看这是怎么回事。

首先想到的是,0代表着NULL,涉及到指针的时候NULL总是比较可怕的,可是这里却异常地安全,这到底是怎么回事?这里的0是个常量,把它换成其它的常量(比如字符串常量),初始化的变量,换成未初始化的变量还会得到这个输出结果吗?编译器是如何处理它的?

这里都是用的->运算符,换成.运算符可以吗?->.运算符在编译器生成代码的时候有哪些不同的表现?这里的这个struct比较特殊,不需要编译器进行内存地址对齐,那么对于那些不规范的structclass , union, enum,这段代码会有什么样的表现?对于嵌套的strict,又会怎样?

于是写了如下一段测试代码:

#include <stdio.h>

 

struct s{

    int a;

    int b;

    char c[8];

    void* d;

}mystruct;

 

struct alignMem {

    char ch;

    int i;

    double f;

};

 

struct nested {

    int i;

    char ch;

    struct alignMem a;

    struct {

       int n;

       char cc;

    } test;

};

 

#define p(exp) printf("%30s:/t%X/n/n", #exp, exp)

 

int main() {

    int i;

 

    p(&((struct s*)0)->d);

    p(&((struct s*)1)->d);

    p(&((struct s*)NULL)->d);

    p(&((struct s*)10)->d);

    p((struct s*)NULL);

    p((struct s*)1);

 

    printf("i is a local variable without initialized:/n");

    p(((struct s*)&i));

    p(&((struct s*)&i)->d);

 

    i = 0;

    printf("i is initialized to 0:/n");

    p(&((struct s*)&i)->d);

    i = 1;

    printf("i is initialized to 1:/n");

    p(&((struct s*)&i)->d);

 

    printf("const char[] as argument:/n");

    p(&((struct s*)"a")->d);

 

    printf("alignmen:/n");

    p(&((struct alignMem*)0)->f);

 

    printf("nested:/n");

    p(&((struct nested*)0)->test.cc);

 

    return 0;

}

 

为了看看编译器是怎么处理的,需要看看编译器生成的汇编代码是如何的,这里的编译器用的是VC 6.0。在命令行下用如下命令编译:

cl /Faasm.txt /FAcs /Fmmap.txt /Tctest.cpp

其中/Fa表示生成汇编代码,/FAcs表示生成的汇编代码中包括机器指令和源代码,/Tc表示强制使用C语言编译器而不是C++编译器来编译。

运行test.exe,输出结果如下:

&((struct s*)0)->d            : 10

 

&((struct s*)1)->d            : 11

 

&((struct s*)NULL)->d         : 10

 

&((struct s*)10)->d           : 1A

 

(struct s*)NULL               : 0

 

(struct s*)1                  : 1

 

i is a local variable without initialized:

((struct s*)&i)               : 12FF7C

 

&((struct s*)&i)->d          : 12FF8C

 

i is initialized to 0:

&((struct s*)&i)->d           : 12FF8C

 

i is initialized to 1:

&((struct s*)&i)->d           : 12FF8C

 

const char[] as argument:

&((struct s*)"a")->d          : 4071F0

 

alignmen:

&((struct alignMem*)0)->f     : 8

 

nested:

&((struct nested*)0)->test.cc : 1C

 

测试结果,我感觉应该能表示这是编译器相关而不是语言相关的东西。而且这应该是一个好的编译器理所当然有的,感觉。上面凡是有地址的,输出的都是和内存地址相关的,那么必须得作一次减法才能得到偏移地址,凡是无地址的,得到的都是“净”的量。

编译器内部都会维护关于类型的信息。从产生的汇编代码也可以看到,凡是(struct s*)0这样的写法,这些值都是在编译时确定的,而用到变量i的时候,都是在运行时确定的。

不管怎么样,这个题目让人学到太多的东西了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值