中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > C/C++
挑战高手,看一行看不懂的代码.
作者:未知 时间:2005-09-13 19:18 出处:ChinaUnix.net 责编:chinaitpower
              摘要:挑战高手,看一行看不懂的代码.

#defiine test(z,w) (&(((z*)0)->w))

这个宏是什么意思?怎么用它?为什么用它?

 jzsun 回复于:2003-02-24 12:16:10
应该是这个意思:z是个数据类型,o是一个结构指针,w是结构o的一个域,上面宏的意思是先把o的类型做一个强制转换,转换成z类型的指针,然后去取其中的w域,并进一步取出W域的地址出来。之所以用了那么多的括号是为了代入不同的Z和W,没有括号的话宏展开时就会出错。

 无双 回复于:2003-02-24 12:20:36
你在哪里看到的

为什么要把0转换指针呢

 menp9999 回复于:2003-02-24 12:20:58
[quote:bce20d43ac="HopeCao"]#defiine test(z,w) (&(((z*)0)->w))

这个宏是什么意思?怎么用它?为什么用它?[/quote:bce20d43ac]
你最好把他实际使用的情况贴出来,有好多东西,不结合上下文,不好理解,我来猜测一下:
Z是数据类型,0我估计是某些C语言的扩充用法,表示结构体或联合体的位移为0,其实就是他的指针,如果上面猜的是对的话:
那么这个宏的作用就是取结构体的分量W的地址.

 menp9999 回复于:2003-02-24 12:22:05
[quote:e99e312a32="jzsun"]应该是这个意思:z是个数据类型,o是一个结构指针,w是结构o的一个域,上面宏的意思是先把o的类型做一个强制转换,转换成z类型的指针,然后去取其中的w域,并进一步取出W域的地址出来。之所以用了那么多的括号是为了..........[/quote:e99e312a32]
是0不是o,

 zhxlanjuan 回复于:2003-02-24 12:37:13
其实,这个问题很简单,给大家看一个例子:

#define offsetof(s, m)  (size_t)(&(((s *)0)->m))

这是offsetof的标准实现,主要是计算出结构体里成员的相对地址偏移量。

这里使用0只是一个使用的技巧,方便计算出偏移量。

楼主的那个宏和这个大同小异。

 jzsun 回复于:2003-02-24 13:10:17
[quote:1cd5e3653e="menp9999"]
是0不是o,[/quote:1cd5e3653e]
 不好意思,看错了。

 menp9999 回复于:2003-02-24 13:12:27
[quote:ab6d6d498d="zhxlanjuan"]其实,这个问题很简单,给大家看一个例子:

#define offsetof(s, m)  (size_t)(&(((s *)0)->m))

这是offsetof的标准实现,主要是计算出结构体里成员的相对地址偏移量。

这里使用0只是一个使用的技巧..........[/quote:ab6d6d498d]
哈哈,我猜对了,可是我还是低手.

 menp9999 回复于:2003-02-24 13:18:05
[quote:c2aaa7a456="jzsun"]
 不好意思,看错了。[/quote:c2aaa7a456]
没啥,我也是在瞎猜的,

 nile 回复于:2003-02-24 13:48:50
楼上的都是高手, 别谦虚~

 杀人名医 回复于:2003-02-24 13:51:30
相对地址偏移量的概念能不能详细的解释一下,结合上面说的,多谢了。

 stanleylei 回复于:2003-02-24 14:45:16
根据成员的地址偏移量以及地址可以计算出宿主结构的地址:
成员地址 - offset = 宿主地址

 杀人名医 回复于:2003-02-24 15:25:36
那宏定义的是   成员的地址  还是   地址的偏移量,另size_t是什么意思,
0具体指什么?能不能再明确的答一下?

 shanhan 回复于:2003-02-24 16:12:19
其实,这个问题很简单,给大家看一个例子:

#define offsetof(s, m) (size_t)(&(((s *)0)->m))

这是offsetof的标准实现,主要是计算出结构体里成员的相对地址偏移量。

这里使用0只是一个使用的技巧,方便计算出偏移量。

楼主的那个宏和这个大同小异。


老大!能跟我们讲解一下吗!
谢谢了!

 zhxlanjuan 回复于:2003-02-24 18:48:43
各位,不好意识,我在解释一下,
#define offsetof(s, m)  (size_t)(&(((s *)0)->m))
这个宏用来求一个结构体成员相对于这个结构体首地址的偏移量,
例如:
struct zhx{
int lanjuan;
};
这个结构体里的成员lanjuan,它的相对偏移量就是4;
这个宏里的0是地址,(s *)0,这一步是把从0这个地址开始的一块大小的内存解释成这个结构体类型,&(((s *)0)->m),这一步是取这个结构体程序m的地址,结合zhx这个结构体的例子,如果取成员lanjuan的地址,这个地址当然是4,对吧!也就是说,通过这个技巧,我们可以很方便的得到了这个成员偏移量。

size_t其实就是无符号整形,也就是把上面得到的那个地址转成无符号整形,楼主的那个宏和这个是一个道理,但显然没这个宏定义的更加严密!

 flw 回复于:2003-02-24 19:43:40
设有结构变量

struct tagX
{
......
<typeY> y;
......
}x;

其中 typeY 是任意一个合法的类型名。

假设 ptrx 是指向 x 的指针。

那么,[u:db21dd5e03][color=darkred:db21dd5e03]分量 y 的偏移量[/color:db21dd5e03][/u:db21dd5e03][color=red:db21dd5e03]是指[/color:db21dd5e03]:“类型 tagX 的任意一个实例的地址与其分量 y 的地址的差”。

对于实例 x 来说,就是 &(ptrx->y) - ptrx。
------------------------- ^ ------ ^
----------------------   实例的地址  分量的地址

如果我们用 offset 来表示这个差值,就有

      offset = &(ptrx->y) - ptrx

这一点是显而易见的。
特别的,当 ptrx 等于 0 时(虽然事实上 ptrx 永远也不会等于 0 ),
我们有:

      offset = &( 0->y )

由于常量 0 的类型必须明确指定,所以就写作

      offset = &( ((struct tagX *)0) -> y )

说到这儿就比较明显了,如果我们定义宏

# define offset     (&( ((struct tagX *)0) -> y ) )

那么该宏的值就是 struct tagX 结构中的分量 y 相对于实例首址的偏移量。
其中 tagX 和 y 只是一个例子,一个更广泛使用的做法是用含有参数的宏将
tagX 和 y 代换:

# define offset( tagX, y )    ( &( ((tagX *)0) -> y )

至于 size_t,它表示一个尺寸的类型,在大多数的系统中,它是基本类型
unsigned int 的一个宏。

 杀人名医 回复于:2003-02-25 10:25:32
完全明白了,多谢诸位高手的回答。

 menp9999 回复于:2003-02-25 10:33:54
[quote:e2a9c7222f="flw"]骸袄嘈?tagX 的任意一个实例的地址与其分量 y 的地址的差”。

对于实例 x 来说,就是 &(ptrx->y) - ptrx。
------------------------- ^ ------ ^
----------------------   实例的地址  分量的地址

..........[/quote:e2a9c7222f]
很好,有高手风范,特别是"实例"一词的使用,很严谨.

 flw 回复于:2003-02-25 16:12:20
兄台过奖了!

 pronetway 回复于:2003-02-26 17:45:51
看来这里还是有C语言的高手

 fanfan 回复于:2003-02-26 22:12:35
我最讨厌的就是宏了 :evil:  :evil:  :evil: ,我宁愿写一个函数也不用它。一旦出错够你查一阵子的

 ohwww 回复于:2003-02-28 22:16:17
学习

 visualit 回复于:2003-03-03 13:52:40
[quote:c6fefb1dd6="HopeCao"]#defiine test(z,w) (&(((z*)0)->w))

这个宏是什么意思?怎么用它?为什么用它?[/quote:c6fefb1dd6]

HopeCao菜鸟, 记得不要脑袋发晕时用这样的macro啊

                                 大鳄有礼啦
                             8)      visualit@21cn.com

 menp9999 回复于:2003-03-03 14:44:27
[quote:a6f50002c8="visualit"]

HopeCao菜鸟, 记得不要脑袋发晕时用这样的macro啊

                                 大鳄有礼啦
                             8)      visualit@21cn.com[/quote:a6f50002c8]
哈哈,这个宏本来就为不轻易脑袋发昏的人用的,所以是挑战呀.免礼免礼,呵呵,我先代HOPE先生收了.

 ibm99 回复于:2003-06-26 11:15:23
精华就是精华!!!

 雪花啤酒 回复于:2003-09-23 13:32:23
哇!好深奥呀!我是新手,(我很痛苦于现状)希望得到大家的帮助,谢谢:如果可能)请问您的信箱或者QQ号之类是多少(可以告诉吗?)我想……我的QQ是252914562 信箱是:ssyeew@citiz.net
我想真诚的和您交朋友!如果……还是谢谢!

您的问题,很抱歉,我是新手,无法帮您解决它,不过,我有一个问题请问您,谢谢回答我!!! 

 ,检索出向某个供应商发出订购单的仓库所在城市,,.... 
我的是 ,(错误啦!):SELECT 城市 FROM仓库WHERE 订购单IN; 
(CELECT订购单FROM订购单WHERE订购单="S4") 
怎么改呀?谢谢!

 wykr3879 回复于:2003-11-15 14:25:15
[quote:568479b1e7="flw"]骸袄嘈?tagX 的任意一个实例的地址与其分量 y 的地址的差”。

对于实例 x 来说,就是 &(ptrx->y) - ptrx。
------------------------- ^ ------ ^
----------------------   实例的地址  分量的地址

..........[/quote:568479b1e7]
表达式中,两个指针的类型不匹配,有的编译器会指出,但有的不会.希望FLW以后改之.

 Lippman 回复于:2004-04-21 22:25:42
其实可以直接使用标准库函数里面的offsetof()来判断结构体里面成员的偏移量。

 someday 回复于:2004-11-21 12:29:51
相对偏移量是个啥玩意?高手?

 linux_newbie 回复于:2004-11-21 18:25:01
[quote:0c98599086]struct zhx{ 
int lanjuan; 
}; 
这个结构体里的成员lanjuan,它的相对偏移量就是4; [/quote:0c98599086]

lanjuan的offset为0。

 assiss 回复于:2004-11-21 18:31:48
有一次,写宏不小心,写错了。
调试了将近一个星期(近万行的程序),就没想到是宏的错。
痛苦……
发誓以后再不用宏代替函数。

 黄山松 回复于:2004-12-22 18:00:13
[quote:2365b54696="assiss"]有一次,写宏不小心,写错了。
调试了将近一个星期(近万行的程序),就没想到是宏的错。
痛苦……
发誓以后再不用宏代替函数。[/quote:2365b54696]
学了一招,感谢楼上的!
assiss,用宏用到如此地步,你不用也罢:)

 javacool 回复于:2004-12-22 20:45:42
那如果进程的0地址不让访问 岂不是这个宏便不能执行?

 flw 回复于:2004-12-23 10:02:07
[quote:bacb80ac8c="javacool"]那如果进程的0地址不让访问 岂不是这个宏便不能执行?[/quote:bacb80ac8c]
这个宏不访问指针。
所以,不用管“让不让访问”。

 converse 回复于:2004-12-23 10:09:54
其实也可以不用0,其它的也行,用0方便产生偏移罢了.

看一看我的博客:
http://null.blogchina.com/blog/article_64083.294122.html
http://null.blogchina.com/blog/article_64083.294577.html

 assiss 回复于:2004-12-23 11:16:32
[quote:79b28ee8ba="flw"]
这个宏不访问指针。
所以,不用管“让不让访问”。[/quote:79b28ee8ba]我觉得,说成:这个宏不访问指针指向的内存。
似乎更恰当一点。

 flw 回复于:2004-12-23 11:32:29
[quote:4b7b460260="assiss"]揖醯茫党桑赫飧龊瓴环梦手刚胫赶虻哪诖妗?
似乎更恰当一点。[/quote:4b7b460260]
你错了。
我写那句话的时候,早就预料到有人会咬文嚼字了。
通常我们写 a = *ptr 的时候,实际上是先访问指针变量,再访问指针(也就是地址)的。
我们通常所说的“指针”只不过是“指针变量”的简称。

 assiss 回复于:2004-12-23 12:19:21
[quote:9ec81ae112="flw"]
你错了。
我写那句话的时候,早就预料到有人会咬文嚼字了。
通常我们写 a = *ptr 的时候,实际上是先访问指针变量,再访问指针(也就是地址)的。
我们通常所说的“指针”只不过是“指针变量”的简称。[/quote:9ec81ae112]
但是,楼主的用法里,结果是常量吧?
我现在给你弄糊涂了,到底你所说的指针,是[quote:9ec81ae112]我们通常所说的“指针”只不过是“指针变量”的简称。[/quote:9ec81ae112]
还是
[quote:9ec81ae112]
指针(也就是地址)的
[/quote:9ec81ae112]

 flw 回复于:2004-12-23 12:21:12
我说的是后面的。

 yuxh 回复于:2004-12-23 12:25:01
编译后得到的结果就是一个常数,和指针没有关系

大家说的都是同一码事

 flw 回复于:2004-12-23 12:28:06
[quote:e13dd3da2c="yuxh"]大家说的都是同一码事[/quote:e13dd3da2c]
没错,我和 assiss 说的正是同一码事。

 assiss 回复于:2004-12-23 12:32:21
明白。了解。(擦一把冷汗,呼,长出一口气……)

 flw 回复于:2004-12-23 12:36:17
实际上,-> 运算符本身就有数学运算在里头,
所以不妨把那个宏当作是一个算术表达式,
看起来用到了指针,其实编译以后和指针没关系。
而指针越界只有在运行时才会发生,所以不会出错的。

 lovett 回复于:2004-12-23 14:22:04
虽然俺勉强看得懂那代码,但还是要赞美一句"好帖"
flw讲解得尤其精彩:)很有条不紊

 eagerly1 回复于:2004-12-23 19:10:36
那是否所有的 -> 运算符都可以看作算术运算,这样就跟指针没有关系,也就不访问指针了?

 twen345 回复于:2004-12-23 21:03:21
[quote:cac932391f="flw"]实际上,-> 运算符本身就有数学运算在里头,
所以不妨把那个宏当作是一个算术表达式,
看起来用到了指针,其实编译以后和指针没关系。
而指针越界只有在运行时才会发生,所以不会出错的。[/quote:cac932391f]
这个,偶还是不是很明白 :em14: 
[code:1:cac932391f]((z*)0)->w  //段错误[/code:1:cac932391f]

[code:1:cac932391f]&((z*)0)->w //正确[/code:1:cac932391f]
个人感觉是"&"而不是"->"使表达式成为了常量而不用在运行时访问内存。

 yeath 回复于:2004-12-23 21:59:52
不就是offset?

 aladding 回复于:2004-12-24 09:52:22
还是有点不明白,假如结构里面有多个变量,变量在实际的储存时可能不是按照顺序排列,那样偏移不是错了?

 flw 回复于:2004-12-24 09:56:44
[quote:728e113b9e="aladding"]还是有点不明白,假如结构里面有多个变量,变量在实际的储存时可能不是按照顺序排列,那样偏移不是错了?[/quote:728e113b9e]
假如结构里面有多个变量,变量在实际的储存时[color=red:728e113b9e]不[/color:728e113b9e]可能不是按照顺序排列

 javacool 回复于:2004-12-24 13:12:51
twen345 说的两种类型不同
((z*)0)->w  //段错误 
涉及到了0代码的内存访问,这是不允许的
而代码: 
&((z*)0)->w //正确 
我觉得是编译器识别了用户取((z*)0)->w地址的意图,编译器在编译时是知道结构内部各变量的相对位置的,所以这里编译成汇编代码时他直接填入了当0地址的w地址值,因而执行时没有涉及到访问内存操作

 javacool 回复于:2004-12-24 13:15:27
看看上面converse的两篇文章,觉得对理解这个问题有帮助

 converse 回复于:2004-12-27 13:33:15
[code:1:bd9c289769]
((z*)0)->w //段错误
&((z*)0)->w
[/code:1:bd9c289769]
如果把((z*)0)->w看作一个表达式的话,那么前者就是求表达式的值,而后者就是求表达式的地址,既然编译器知道了是求的地址而不是值就不会真正的对地址0进行解引用,而是得到这个地址然后根据结构中的偏移值来得到这个成员的地址。

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有