程序员面试宝典的问题之一,做的时候觉得有些小问题不懂,就搜了一下,整理整理,再加一点自己的理解。
题目:
用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量
如:struct student
{
int a;
char b;
double c;
}
则:
FIND(student,a); //等于0
FIND(student,b);//等于4
答案:#define FIND( struc, e ) (size_t)&(((struc*)0)- >e)
首先,ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,所以我们的第一步(struc*)0,就是把0转换为一个struc类型的指针。利用这个访问结构体里面元素的话,是非法的。参考文献:http://blog.csdn.net/huichengongzi/article/details/7478739 里面提到:“&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常 量)地址,这样就完全避免了通过NULL指针访问内存的问题。”
参考文献:http://www.cnblogs.com/alexqdh/archive/2011/04/30/2029447.html 里面提到,如果不加(size_t)则输出的是十六进制
这样对访问第一个元素是没问题的,但是如果访问元素b,或者元素c时,编译器会报错,原因可能是我们通过NULL指针访问了结构体中的元素(具体没搞明白,汇编那块学的不太好)。
所以我觉得(size_t)这个强制类型转换是必须加的,要不然编译器会认为你在利用NULL指针访问结构体。
具体代码:
#include <iostream>
using namespace std;
struct student{
int a;
char b;
double c;
};
#define findoffset(stru,e) ((size_t)&((stru*)0)->e)
//#define findoffset(stru,e) (&((stru*)0)->e)
int main()
{
cout<<findoffset(student,a)<<endl;
cout<<findoffset(student,b)<<endl;
cout<<"ans is:"<<endl;
//测试null指针是否能够访问结构体第二个元素
// cout<<&(((student*)0)->b)<<endl;
student *p,stu;
stu.a = 10;
stu.b = 'e';
stu.c = 1.0;
p = &stu;
cout<<&(p->a)<<endl;
//cout<<findoffset(stu,a)<<endl;
//cout<<findoffset(stu,b)<<endl;
return 0;
}
本人比较爱乱想,因此加了一个看看是否能通过结构体实例进行偏移量计算,结果是不可以,具体原因请将定义的宏带入就可以知道,实例名并不是类型,所以编译器在进行指针转换的时候会不知所措,而报错。。
总结一下:
1、(type*)0,将0转换为type类型指针,且其不能访问结构体内部数据,只能将地址转换为十进制数输出。
2、结构体实例不能作为所定义的宏里面的变量来计算对应的偏移量。
个人浅见,不一定正确。