求解:sun的一道笔试题,thanks:)
写一个带参数宏get_struct_addr_from_member_addr(p, stru, m),
能够根据任意结构实体的某一个成员的地址,算出该结构实体的地址,其中参数p是指向该
成员的指针,stru是该结构体,m是该成员。
问题点数:20、回复次数:50Top
1 楼oo(为了名副其实,努力学习oo技术ing)回复于 2006-04-04 13:37:29 得分 0
(struct stru*)( (char*)p - (char*)&( (( struct stru*)0)->m) ) )Top
2 楼universes(及时揭帖是一种美德 | CSDN也这么黑)回复于 2006-04-04 13:40:56 得分 0
(stru *)((char *)p-&((stru *)0)->m))
在《linux内核源代码情景分析》里看到过,有点忘了
根据自己的想法写了一个,忘高手指教。Top
3 楼universes(及时揭帖是一种美德 | CSDN也这么黑)回复于 2006-04-04 13:44:03 得分 0
to OO:
呵呵,你抢先了。
请问一下后面那个(char *)好像就没什么必要了吧?另外感觉你的那个struct也是多余的。。。。Top
4 楼YufengShi(浪子)回复于 2006-04-04 13:48:14 得分 0
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#define get_struct_addr_from_member_addr(p, stru, m) \
(stru*)((char*)p-offsetof(stru, m))Top
5 楼oo(为了名副其实,努力学习oo技术ing)回复于 2006-04-04 13:51:48 得分 0
后一个加char*是因为m的类型不知道,char* - int*会怎么样?没试过。
帖子在c版,所以加了struct,也许题目的意思是不需要加吧,但题目也没说明确Top
6 楼YeTimmy()回复于 2006-04-04 13:51:50 得分 0
大大的牛人。。
原来(( struct stru*)0)->m可以算出结构体的偏移量
OO的后面好象多了个)Top
7 楼vision2004()回复于 2006-04-04 13:56:13 得分 0
linux内核中就有这个实现,当初看到他的时候,惊叹不已,呵呵Top
8 楼gjianpro(#ifndef _DEBUG)回复于 2006-04-04 13:57:44 得分 0
to universes;:
oo的写法是规范的,,后面的(char *)是必须的,,而两个struct都是可有可无的Top
9 楼jinjiajie(leorio)回复于 2006-04-04 14:05:28 得分 0
能解释一下吗?Top
10 楼jinjiajie(leorio)回复于 2006-04-04 14:34:01 得分 0
哦...了解了...不用解释了Top
11 楼cunsh(村少)回复于 2006-04-04 14:36:34 得分 0
mark
xuexi;Top
12 楼universes(及时揭帖是一种美德 | CSDN也这么黑)回复于 2006-04-04 14:42:00 得分 0
to OO and gjianpro:
我觉得不用char *,而用int或许更合理。。。。
另外:
YufengShi(浪子)转换为size_t就感觉有点不合适了,我在BSD中看的代码size_t最终其实是:unsigned int或者unsigned long long,即为unsigned,而这里的地址在某些系统里会是一个负数。。。。。
抛砖引玉,哪位再请赐教?
Top
13 楼hsilz(三星五角▲▲▲★★★★★自己加星玩)回复于 2006-04-04 14:44:56 得分 0
#define list_entry(ptr,type,member)
((type*)((char*)(ptr)-(ulong)(&((type*)0)->member)))
Top
14 楼thinkinnight(逍遥)回复于 2006-04-04 14:58:15 得分 0
#define get_struct_addr_from_member_addr(p, stru, m)\
( (struct stru*)( (char *)p - (unsigned long)(&((struct stru*)0)->m) ) )Top
15 楼yleiou(单刀匹马)回复于 2006-04-04 15:19:11 得分 0
厉害 惊叹Top
16 楼hehe(呵呵)回复于 2006-04-04 15:28:57 得分 0
markTop
17 楼lyae(阿因)回复于 2006-04-04 15:55:10 得分 0
惊艳!...谦虚.Top
18 楼wqrz_015()回复于 2006-04-04 16:22:52 得分 0
//--winnt.h
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(UINT_PTR)(&((type *)0)->field)))
Top
19 楼lyae(阿因)回复于 2006-04-04 16:29:27 得分 0
to: oo(为了名副其实,努力学习oo技术ing)
//=================================================================
后一个加char*是因为m的类型不知道,char* - int*会怎么样?没试过。
帖子在c版,所以加了struct,也许题目的意思是不需要加吧,但题目也没说明确
//=====================================================================
其实m的类型不知道不是没有关系吗? "&(((struct stru*)0)->m)"得到的已是地址偏移值,是什么指针类型没有关系,第一个"(char *)"保证了地址计算正确(按char 1字节).第二个"(char *)"好像就没有必要了.Top
20 楼citywanderer2005(流浪狗)回复于 2006-04-04 16:31:27 得分 0
恩,厉害!Top
21 楼leaway211(Ludwig)回复于 2006-04-04 16:39:26 得分 0
(stru *)( (char *)p - (char*)&(((stru *)0)->c) )Top
22 楼fxyj2008(超级黑客)回复于 2006-04-04 19:58:51 得分 0
说实话,学了几年的C,还真不明白struct stru*)0)->m的意思,谁能给我解释一下?
长长见识啊Top
23 楼sea_sharka_17()回复于 2006-04-04 20:54:20 得分 0
真得是学习啊!Top
24 楼caryyang(我爱排骨)回复于 2006-04-04 21:09:49 得分 0
linux内核的通用队列实现Top
25 楼zhouyinhui(我也飘~~过)回复于 2006-04-04 21:16:15 得分 0
回复人:oo(为了名副其实,努力学习oo技术ing)
(struct stru*)( (char*)p - (char*)&( (( struct stru*)0)->m) ) )
-----------------------
多了一个 )
Top
26 楼minsavage(帆野)回复于 2006-04-04 21:29:11 得分 0
(struct stru*)( (char*)p - (char*)&( (( struct stru*)0)->m) ) )
如果没有第2个(char *) 在编译器里就会报错 告知减号两端类型不匹配
至少在VC6下是报错了哈Top
27 楼jruv(~~~一叶落而知天下秋~~~)回复于 2006-04-05 00:47:16 得分 0
我的想法是其实0可以换成任意值N
数据成员偏移量是
unsigned long offset = (char*)&( (( struct stru*)N)->m)-(char *)N
所以结构地址是(struct stru*)( (char*)p - offset )
要这样理解:
把N当作一个地址,强制转换成结构类型struct stru的首地址, 则&(struct stru*)N)->m就是m成员的地址,那这个数据成员的偏移量就很清楚了
就是(char*)&( (( struct stru*)N)->m)-(char *)N
偏移量知道了,那结构的地址就是这个成员的绝对地址p减去这个偏移量喽
(struct stru*)( (char*)p - offset )
0的情况只不过是特例而已,比用N方便,其实用上面的公式N都不需要有赋初值的,不过会有警告出现.
Top
28 楼cquazhi(阿志)回复于 2006-04-05 09:54:13 得分 0
晕了,我怎么看不懂啊???大牛能不能详细解释一下啊,我初学C呢Top
29 楼september___29()回复于 2006-04-05 10:38:42 得分 0
这儿有个解释...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
A_B_C_ABC(黄瓜) ( ) 信誉:100
#define CONTAINING_RECORD(address, type, field) \
((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=
这个,在我弄了两年多的Cpp的还没有遇到有这样的NB需求,想先问问你朋友在用这个东西的context是何?期待
但是,你既然问了
我是这样理解的:
例如:
class CRecord{
public:
int m_Age;
string m_Name;
image m_Pic;
//...
//....其它
}
是个简单的记录类型,
有
void Test(){
CRecord objRec;//记录对象..
image& rImage = objRec.m_Pic;//记录对象里面的##
CRecord* pRecord = CONTAINING_RECORD(&rImage, CRecord, m_Pic);
//也就是 CRecord* pRecord =
//((CRecord*)((char*)(&rImage) - (unsigned long*)(&((CRecord*)0)->m_Pic)))
}
现在我就来解释解释
"(&((CRecord*)0)->m_Pic))"为什么一定要是"(&((CRecord*)0)->m_Pic))"
而不是"(&((CRecord*) -2.99 )->m_Pic))"或者"(&((CRecord*) 168 )->m_Pic))"吧,好不好?
第一.Cpp的C血统是很深厚的...C在我们的电脑上无所不能,Cpp一样...掌握它..
不知道你平时看到C**Veiw* p**Wnd=new C**Veiw然后 使用 p**Wnd->m_myMenber的时候有没有思考
"到底是怎么回事儿"
因为你的P**Wnd指针本身除了保存一个地址(一个整型值)外什么信息都没有,
既而什么信息都没有那它怎样引用的它所指向的m_myMember对象呢?
其实,它是通过一种位偏移来得到的,"m_myMenber"名称自己本身就带有偏移的信息了
p**Wnd->m_myMember其实就是在 p**Wnd指针保存的值加上m_myMember的偏移值而获取m_myMember这个对象的..这是理论..
第二.((CRecord*) "0" )这句话告诉了你的机器,
从现在开始 在内存中 从[0,1,2,3,4,....sizeof(CRecord)-1]这一块内存(注意是从"零"开始的)是个"CRecord类生成的对象的内存块"(实际上这块内存很可能是保存的其它的信息,而且是你去修改就很可能感觉比较郁闷的信息)..
((CRecord*) 0 )->m_Pic)这句话告诉你的机器在[0,1,2,3,4,....sizeof(CRecord)-1]这块内存找到一个m_Pic对象(实际上也不存在这个m_Pic对象,没有关系)
(&((CRecord*)0)->m_Pic))返回上面你找到的虚拟的"m_Pic对象"的地址,但是要注意(CRecord*) 0是(CRecord*) 0,而不是(CRecord*) 12也不是(CRecord*) -9.99;所以(ULONG_PTR)(&((CRecord*)0)->m_Pic))实际上是一个偏移量的值!!
然后当前你m_Pic地址减去你的偏移量当然就是包括m_Pic的包容类的对象的地址拉
(注意包容类的地址必须强制转换为(char*)也就是(PCHAR)(address)不能直接用(address)
(why? 这个你应该明白的,再要我说就是对其它人智商的侮辱了,对不??)
完了在把它强制转换到(type *)类型也就是(CRecord*)类型
这样说下面的语句应该是很明白了
((CRecord*)((char*)(&rImage) - (unsigned long*)(&((CRecord*)0)->m_Pic)))
((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
但是我开始说了用下面的方法好一些,也好明白一些;
image CRecord::*pMb = &CRecord::m_Pic;
CRecord* pRec=(CRecord*)((int)&objRec.m_Pic- *(int*)&pMb);
你说呢?
----
看来sun 也不难进啊Top
30 楼september___29()回复于 2006-04-05 10:55:12 得分 0
哈哈,看了楼上sep_29的解答是不是有醍醐灌顶之大觉大悟之感啊Top
31 楼hotonion(菜鸟怎么变大牛呢?)回复于 2006-04-05 11:16:57 得分 0
markTop
32 楼qingyuan18(zealot_tang)回复于 2006-04-05 13:17:56 得分 0
靠,牛人!Top
33 楼skyofwind(风之吻)回复于 2006-04-05 13:44:23 得分 0
佩服的无语言了哈,一个字。。。。强!!Top
34 楼cattlenzq(吃狼的豆腐(不要给分了,散起来真麻烦!))回复于 2006-04-05 14:34:05 得分 0
markTop
35 楼cutenoob(cute )回复于 2006-04-05 14:39:47 得分 0
markTop
36 楼kentxp(kent)回复于 2006-04-05 15:03:49 得分 0
mark 方便查找Top
37 楼noneone(noneone)回复于 2006-04-05 15:18:34 得分 0
markTop
38 楼mengge(踏岸寻柳)回复于 2006-04-05 17:12:16 得分 0
不错,毕竟的大公司的题目!Top
39 楼cg5353(努力学习中)回复于 2006-04-05 18:47:33 得分 0
markTop
40 楼fxyj2008(超级黑客)回复于 2006-04-05 19:18:48 得分 0
(struct stru*)( (char*)p - (char*)&( (( struct stru*)0)->m) ) )
看了各位的答案,稍微理解了一下,
但是觉得很奇怪,将常数转化为struct stru* 类型,这完全是编译器决定答案的啊,这种题目还有意思的啊,假如我重新设计一个编译器,那答案是不是变了呢??
竟然还有人称什么 强 ,,牛人 。。 大公司的 题目
如果这也算什么强的话,那设计编译器的人呢,写OS的人呢??
真正强的人应该是对计算机系统有很深的理解,比如写编译后端的人!
可惜还没有看到国内有什么商用的编译器哦!Top
41 楼whopkl(清明)回复于 2006-04-05 19:59:22 得分 0
牛人一个Top
42 楼ruodeer(看我的个性签名都给我分)回复于 2006-04-05 22:40:24 得分 0
markTop
43 楼closetome(即鹿无虞,惟入于林中。君子几,不如舍。往吝。)回复于 2006-04-05 23:02:49 得分 0
mark
正在看linux,这个宏风格应该是linux下的Top
44 楼wangever(环环)回复于 2006-04-06 01:37:06 得分 0
markTop
45 楼mengge(踏岸寻柳)回复于 2006-04-06 08:25:19 得分 0
说白了吧,精髓只在这里:
(size_t)&(((struct stru *)0)->m) // 偏移量
这里的这个0,似乎不可等闲视之,应该是:
(size_t)&(((struct stru *)NULL)->m)
因为,#define NULL (void *)0
你试一试将0换做别的数字,看看结果是否还是正确的。Top
46 楼howyougen(夫孝,德之本也,教之所由生也)回复于 2006-04-06 08:51:31 得分 0
markTop
47 楼lemon_1104()回复于 2006-04-13 19:52:44 得分 0
学习Top
48 楼HopeInDark(子弹)回复于 2006-04-14 10:00:42 得分 0
MARKTop
49 楼renwxing()回复于 2006-04-14 11:55:01 得分 0
#define list_entry(ptr,type,member) \
( (type*)( (char *)(ptr) - (unsigned long)(&((type*)0)->member) ) )
(char *) and (unsigned long)
(char *) and (char *)
(unsigned long) and (unsigned long)Top
50 楼gamedr()回复于 2006-04-14 13:04:31 得分 0
学习
Top




