<stddef.h>一日游

"没有升迁发财,没有大牛小白,挖掘工程的背后是回归最初兴趣的源动力。“

                                                                                       ----火星人

===<stddef.h>简介===

<stddef.h>不算是旅游胜地,但在著名旅游指南cplusplus.com上搜索一把还是能看到相应的介绍:

 

此篇就是dig一下gcc下这位大家闺秀的方方面面,找点乐子(被那些无聊的工作闷得受不了的Mars点了一下头)。

看看需要stddef.h的男士很多,随便来个

grep "#include <stddef.h>" -r /usr/include

就保证可以找到一堆:

<stddef.h>的“实现”在不同平台上的差异造就了她端庄外表背后的奔放万千。

 

===参数化类型定义===

因为上一篇文章是从Mac OS X开始,而且在末尾承诺这一篇给红帽帮一个交代,于是信守承诺从不跳票的Mars今天就从redhat开始<stddef.h>一日游。

首先找找<stddef.h>的地理位置,还是老招数啦 find /usr -name "stddef.h",gcc下的<stddef.h>位于:

/usr/lib/gcc/x86_64-redhat-linux/4.1.1/include/stddef.h

这也是广大猿猴coding时涉及到的那位stddef.h(没错,你猜对了,还有其他的,但那不是今天的焦点......)。

马上进入到<stddef.h>的世界,跳过开头无趣的注释,马上就看到大门上牌子写着:

 28 /*
 29  * ISO C Standard:  7.17  Common definitions  <stddef.h>
 30  */
 31 #if (!defined(_STDDEF_H) && !defined(_STDDEF_H_) && !defined(_ANSI_STDDEF_H) \
 32      && !defined(__STDDEF_H__)) \
 33     || defined(__need_wchar_t) || defined(__need_size_t) \
 34     || defined(__need_ptrdiff_t) || defined(__need_NULL) \
 35     || defined(__need_wint_t)

虽然要火星人从代码转换成地球语言有点吃力,但还是愿意一试。

........??.......!....!!!........

大概意思就是:“如果你没来过一把stddef定义,那就来一手吧!或者如果你需要什么类型,也来一手吧!“

大伙估计又认出来这几个混球,怎么又是他们!上一篇文章不已经出现过了吗,就是几个通常版本下GCC都会有的预定义宏对应的数据类型。没错,人家宏是预定义了,但具体编程类型还没有呢,所以还得玩一把typedef,原来<stddef.h>就是干这事,当然事情没有这么简单.......

先回头看看这卡在整份代码开头的“开关”,从结构看来,这个<stddef.h>很可能是一工具——只要外部代码不确定自己有没有哪个类型的定义,就把__need_*定义一下,然后include这个header从而保证类型的存在,想到这里Mars眼里闪现出一丝OOC的光芒。什么是OOC,这年头都喜欢用缩写震人,不就是面向组件编程。<stddef.h>作为预编译级别的可复用组件为人民服务,Mars学习了,大伙呢?

紧接在后面的下一道“宏开关”(Mars爱上这词了)也证明了上面一些观点,如果外部神马都不__need,那就直接完成"STDDEF_H系列"的定义(你看这些前前后后的下划线.....)。

 37 /* Any one of these symbols __need_* means that GNU libc
 38    wants us just to define one data type.  So don't define
 39    the symbols that indicate this file's entire job has been done.  */
 40 #if (!defined(__need_wchar_t) && !defined(__need_size_t)        \
 41      && !defined(__need_ptrdiff_t) && !defined(__need_NULL)     \
 42      && !defined(__need_wint_t))
 43 #define _STDDEF_H
 44 #define _STDDEF_H_
 45 /* snaroff@next.com says the NeXT needs this.  */
 46 #define _ANSI_STDDEF_H
 47 /* Irix 5.1 needs this.  */
 48 #define __STDDEF_H__
 49 #endif

最初,Mars以为没有任何__need_*,那么后续的编译逻辑就会什么类型都不定义,然而这就太低估设计了。事实上在后面的编译代码里面,每一个类型的真正typedef之前都会有这样一个开关:(下面挑了其中最出名的size_t作为例子,其他的类型几乎一样)

169 /* Define this type if we are doing the whole job,
170    or if we want this type in particular.  */
171 #if defined (_STDDEF_H) || defined (__need_size_t)
172 #ifndef __size_t__      /* BeOS */
173 #ifndef __SIZE_T__      /* Cray Unicos/Mk */
174 #ifndef _SIZE_T /* in case <sys/types.h> has defined it. */
175 #ifndef _SYS_SIZE_T_H
176 #ifndef _T_SIZE_
177 #ifndef _T_SIZE
178 #ifndef __SIZE_T
179 #ifndef _SIZE_T_
180 #ifndef _BSD_SIZE_T_
181 #ifndef _SIZE_T_DEFINED_
182 #ifndef _SIZE_T_DEFINED
183 #ifndef _BSD_SIZE_T_DEFINED_    /* Darwin */
184 #ifndef _SIZE_T_DECLARED        /* FreeBSD 5 */
185 #ifndef ___int_size_t_h
186 #ifndef _GCC_SIZE_T
187 #ifndef _SIZET_
188 #ifndef __size_t
189 #define __size_t__      /* BeOS */
190 #define __SIZE_T__      /* Cray Unicos/Mk */
191 #define _SIZE_T
192 #define _SYS_SIZE_T_H
193 #define _T_SIZE_
194 #define _T_SIZE
195 #define __SIZE_T
196 #define _SIZE_T_
197 #define _BSD_SIZE_T_
198 #define _SIZE_T_DEFINED_
199 #define _SIZE_T_DEFINED
200 #define _BSD_SIZE_T_DEFINED_    /* Darwin */
201 #define _SIZE_T_DECLARED        /* FreeBSD 5 */
202 #define ___int_size_t_h
203 #define _GCC_SIZE_T
204 #define _SIZET_
205 #if defined (__FreeBSD__) && (__FreeBSD__ >= 5)
206 /* __size_t is a typedef on FreeBSD 5!, must not trash it. */
207 #else
208 #define __size_t
209 #endif
210 #ifndef __SIZE_TYPE__
211 #define __SIZE_TYPE__ long unsigned int
212 #endif
213 #if !(defined (__GNUG__) && defined (size_t))
214 typedef __SIZE_TYPE__ size_t;
215 #ifdef __BEOS__
216 typedef long ssize_t;
217 // 无尽的#endif......

先放下你们手中的鸡蛋!Mars也知道把这样裹足一样的代码贴上来显然是江湖卖药,但这也是精心考虑的(甚至已经为不贴上来写了一段话.........),最后还是觉得需要有所对照比较好,毕竟一众宅不一定有这兴趣去亲自看代码,从而产生误解。

上面这样的一长串宏定义暂且称他为火车宏,除了size_t以外,ptrdiff_t、wchar_t和wint_t都各自有自己一套火车宏。在上面的火车头有一段被加粗的注释和宏代码,不难理解,只要没有任何__need_*被定义,那么_STDDEF_H一定存在,从而任何类型的火车都会被启动……

这样的设计Mars觉得很不错,起码省了一个__need_all,对吧!?

在这些火车宏与开场的宏检查代码之间还有一段70多行,闪亮亮的hacking(or patching?),里面体现了地球圈对GCC的支持和贡献,这些代码都是针对不同的OS、硬件平台做出判断并做出修正,分别由GCC社区中各路侠客所贡献,希望Mars有一天也能在这留下足迹.......

下面三小节Mars尝试挖掘这些hacking的背后,但因为跨的平台不但寡见,而且古老,一个个安装来探究单一的主题有点奢华,还好还是有一点方法代替实际装备上这些老古董。

 

===兼容<sys/stdtypes.h>===

__sys_stdtypes_h历史:

51 #ifndef __sys_stdtypes_h
52 /* This avoids lossage on SunOS but only if stdtypes.h comes first.
53    There's no way to win with the other order!  Sun lossage.  */
    ..............
393 #endif /* __sys_stdtypes_h */

在整个420行左右的stddef.h下这个宏就跨越了整整340多行,眨眼一看还定势思维般把他当成主角,其实__sys_stdtypes_h只不过是个过气跑龙套。

这个开关宏生于<sys/stdtypes.h>,而后者是Sun OS中使用的一个系统内部header(Mars轻轻地加粗了某部位……),用来定义很多“标准”类型,而在Sun OS中由<sys/types.h>来对其进行包含,OS API层设计者的原意当然是让大伙写上可移植的代码,少长几根白发;然而世界各地的coder怎么会乖乖坐在sys/types.h的外面?不少代码出于各种无迹可寻的原因直接包含了<sys/stdtypes.h>,于是在后来将程序移植到Sun OS的好基友——Solaris上的时候大伙纷纷中枪倒地,在各大论坛上寻求帮助,于是在Google上搜索"solaris stdtypes.h"会有很有趣的事情发生……

这top 4的搜索结果里面的内容大致就是........

"The author of the software includes a file called <sys/stdtypes.h> which I have found on Sun OS systems but not on Solaris systems. "

"But there is no stdtypes.h in Solaris 7. "

"Solaris 2.4 is apparently missing a file <sys/stdtypes.h> ."

"What is the equivalent stdtypes.h in Solaris 2.5"

因为Solaris只提供<sys/types.h>,而stdtypes.h挥挥衣袖,不带走一片云彩;只留下那一众白首苍茫,想随它而去的工程师。

扯了半天,其实在<stddef.h>中的那个开关无非就是用来确保接下来做的定义不要跟Sun OS冲突,但是下面的注释很引人注目,Mars花了整整半个小时去理解这段话,尤其花了不少时间去找lossage这个词的翻译,一度还认定是个没文化的人写上去的!!当然,最后没文化的人是谁,观众们知道,就不必多说了.....

注释的意思只有在先包含了<sys/stdtypes.h>的前提下,再包含<stddef.h>才可以保证前者的定义是有效的,安全的,可以给Sun OS happy的;一旦包含的顺序反过来,那么必然会造成<sys/stdtypes.h>中某些类型的定义失败,从而编译错误,然后Sun OS不happy,功能丢失。这就是充满传奇色彩的lossage的含义。

之所以会这样,大概怎么也得看看<sys/stdtypes.h>的真面目,Google上能找到的大抵有以下两种:

  1. ftp://pds.nasa.gov/pub/toplevel/tools/other_tools_retire/Solaris/source/stdtypes.h

  2. http://stuff.mit.edu/afs/sipb/project/gnat/src/gnat-3.05/src/ada/threads/include/pthread/stdtypes.h

其他的一些采用的甚至不是__sys_stdtypes_h这个开关,所以就直接忽略了。第一条里面的<stdtypes.h>是目标,因为那是在sys目录下,而不是pthread下的。第一个link似乎需要FQor VPN才能打开,瞧那域名是什么,嗯,你们懂的。

但无论哪一条里面都做了无法挽回的事情——直接typedef一些重要的标准类型(下面代码的加粗部分),一点检查都没有做。甚至在旁边的注释都是/* ??? */,这爹坑得够狠的,Mars怀疑这注释的意思是不是在问,“怎么搞的,直接就typedef了!?“,估计是GCC里面有人抢了这header作者的女朋友还是什么的,否则用不着如此水火不容啊。

/*      @(#)stdtypes.h 1.6 90/01/04 SMI */

/*
 * Suppose you have an ANSI C or POSIX thingy that needs a typedef
 * for thingy_t.  Put it here and include this file wherever you
 * define the thingy.  This is used so that we don't have size_t in
 * N (N > 1) different places and so that we don't have to have
 * types.h included all the time and so that we can include this in
 * the lint libs instead of termios.h which conflicts with ioctl.h.
 */
#ifndef __sys_stdtypes_h
#define __sys_stdtypes_h

#ifndef SUN_UNIX
typedef int             sigset_t;       /* signal mask - may change */
typedef int             pid_t;          /* process id */
typedef unsigned short  mode_t;         /* file mode bits */
typedef short           nlink_t;        /* links to a file */
#endif

typedef unsigned int    speed_t;        /* tty speeds */
typedef unsigned long   tcflag_t;       /* tty line disc modes */
typedef unsigned char   cc_t;           /* tty control char */


#ifndef SUN_UNIX
typedef long            clock_t;        /* units=ticks (typically 60/sec) */
typedef long            time_t;         /* value = secs since epoch */
typedef int             size_t;         /* ??? */
#endif


typedef int             ptrdiff_t;      /* result of subtracting two pointers */

typedef unsigned short  wchar_t;        /* big enough for biggest char set */

#endif  /* !__sys_stdtypes_h */

尽管size_t或者能从SUN_UNIX宏开关中逃脱,但ptrdiff_t和wchar_t是硬伤。至此,<stddef.h>对<sys/stdtypes.h>简单而必须有瑕疵的兼容已经发掘完成,接下来是<stddef.h>对<machine/ansi.h>的补充性兼容。


===兼容<machine/ansi.h>===

 55 /* On 4.3bsd-net2, make sure ansi.h is included, so we have
 56    one less case to deal with in the following.  */ 57 #if defined (__BSD_NET2__) || defined (____386BSD____) || (defined (__FreeBSD__) && (__FreeBSD__ < 5)) || def    ined(__NetBSD__)
 58 #include <machine/ansi.h>
 59 #endif

这个兼容主要是针对旧OS,就FreeBSD来说就是5之前的版本。下面我们就拿FreeBSD 4.3的i386/include/ansi.h来挖一下有什么有趣的玩意。下面是相应的源码地址:

http://svnweb.freebsd.org/base/release/4.3.0/sys/i386/include/ansi.h?view=co

不是<machine/ansi.h>吗?怎么搞成i386/include/ansi.h?因为machine这个目录是安装OS时根据具体机器平台的判断而决定往里面塞什么东西,因此OS的源码中自然就该对不同机器平台有不一样的,但又能对应的内容。之所以选i386嘛,因为Mars喜欢这个系列!!

<machine/ansi.h>中主要跟stddef.h有勾搭关系的部分是:

#ifndef _MACHINE_ANSI_H_
#define _MACHINE_ANSI_H_

/*
 * Types which are fundamental to the implementation and must be declared
 * in more than one standard header are defined here.  Standard headers
 * then use:
 *    #ifdef    _BSD_SIZE_T_
 *    typedef    _BSD_SIZE_T_ size_t;
 *    #undef    _BSD_SIZE_T_
 *    #endif
 */
#define    _BSD_CLOCK_T_    unsigned long        /* clock()... */
#define    _BSD_CLOCKID_T_  int                /* clock_gettime()... */
#define    _BSD_PTRDIFF_T_  int                /* ptr1 - ptr2 */
#define    _BSD_RUNE_T_     _BSD_CT_RUNE_T_      /* rune_t (see below) */
#define    _BSD_SIZE_T_     unsigned int         /* sizeof() */
#define    _BSD_SOCKLEN_T_  __uint32_t
#define    _BSD_SSIZE_T_    int                 /* byte count or error */
#define    _BSD_TIME_T_     long                /* time()... */
#define    _BSD_TIMER_T_    int                 /* timer_gettime()... */
#define    _BSD_WCHAR_T_    _BSD_CT_RUNE_T_       /* wchar_t (see below) */

注释中公告了这些_BSD_*_T_宏的正确用法——要定义什么标准类型,就检查有没有对应的_BSD版本,有就用上,显然是给那些写标准header的西装革履的家伙看的。大概是因为gcc社区中的hacker都是手执棒棒糖,充满创造力的孩纸,因此在stddef.h中他们采用了另一种方式来应用这些_BSD_*_T_宏,下面拿wchar_t来描述一下这个过程,不能老让size_t抢风头啦。

 55 /* On 4.3bsd-net2, make sure ansi.h is included, so we have
 56    one less case to deal with in the following.  */ 57 #if defined (__BSD_NET2__) || defined (____386BSD____) || (defined (__FreeBSD__) && (__FreeBSD__ < 5)) || def    ined(__NetBSD__)
 58 #include <machine/ansi.h>
 59 #endif
    ............
 65 /* In 4.3bsd-net2, machine/ansi.h defines these symbols, which are
 66    defined if the corresponding type is *not* defined.
 67    FreeBSD-2.1 defines _MACHINE_ANSI_H_ instead of _ANSI_H_ */
 68 #if defined(_ANSI_H_) || defined(_MACHINE_ANSI_H_)
       ............
 75 /* On BSD/386 1.1, at least, machine/ansi.h defines _BSD_WCHAR_T_
 76    instead of _WCHAR_T_. */
 77 #if !defined(_WCHAR_T_) && !defined(_BSD_WCHAR_T_)
 78 #ifndef _BSD_WCHAR_T_
 79 #define _WCHAR_T
 80 #endif
 81 #endif

上面宏定义的逻辑是:对于包含<machine/ansi.h>的代码,如果既没_WCHAR_T_,又没_BSD_WCHAR_T_,那就来一个 _WCHAR_T。为什么要来一个_WCHAR_T?还记得上一节中那延绵不绝的火车宏吧,当然对wchar_t类型也有一样的,而如果定义了_WCHAR_T就阻止了最终对wchar_t的typedef——也就是如果同时没有_WCHAR_T_和_BSD_WCHAR_T_,那就放弃对wchar_t的typedef。(火车宏中每一个小小的开关都能砍断整条)

为何如此?Mars为这个问题抓狂了好一会,到饮料机前踢下了一罐直火拿铁,然后得到了答案(不是广告...)。

回顾前面<machine/ansi.h>开场的指导注释,一旦使用了一个_BSD_XXX_T_来定义对应的标准类型,就得undef这个_BSD_XXX_T_,这就可以解释了,不存在_BSD_XXX_T_就意味着xxx_t类型已经定义!(其实人家都已经在注释上写得“清清楚楚”了,Mars你兴奋个毛毛呢)

好了,那现在<stddef.h>发现_BSD_WCHAT_T_已经定义了,接下来搞啥?

 91 #if defined (__need_wchar_t) || defined (_STDDEF_H_)
 92 #undef _WCHAR_T_
 93 #undef _BSD_WCHAR_T_
 94 #endif

既然有了_BSD_WCHAT_T_存在的依靠,看看外部要不要定义wchar_t,注意这里除了__need_wchar_t,还可以是_STDDEF_H_(为什么?Mars想起了什么,__need_all?),只要满足条件就把相应的宏开关“关掉”,好保证接下来的火车宏畅通无阻,最终到达wchar_t的typedef。

在wchar_t的火车宏里发现了这个现象(注意行号,中间有缩略):

255 #ifndef _BSD_WCHAR_T_
264 #ifndef _GCC_WCHAR_T
272 #define _BSD_WCHAR_T_
278 #define _GCC_WCHAR_T
322 #ifndef __WCHAR_TYPE__
323 #define __WCHAR_TYPE__ int
324 #endif
326 typedef __WCHAR_TYPE__ wchar_t;
363 #if defined(_ANSI_H_) || defined(_MACHINE_ANSI_H_)
374 #ifdef _GCC_WCHAR_T_
375 #undef _WCHAR_T_
376 #undef _BSD_WCHAR_T_
377 #endif

这里突出的重点是_GCC_WCHAR_T,这个宏表示,是不是已经在定义出GCC的wchar_t类型,是的话打上这个宏开关,那就彻底消灭掉_BSD_WCHAR_T_(尽管前面又重新定义他了,估计是为了结构对称),不会再重复定义了。

至此,已经探索了针对wchar_t的<machine/ansi.h>兼容,对ptrdiff_t、size_t和wint_t的兼容也非常类似。

 

===神秘的<sys/_types.h>包含=== 

 55 /* On 4.3bsd-net2, make sure ansi.h is included, so we have
 56    one less case to deal with in the following.  */
 57 #if defined (__BSD_NET2__) || defined (____386BSD____) || (defined (__FreeBSD__) && (__FreeBSD__ < 5)) || def    ined(__NetBSD__)
 58 #include <machine/ansi.h>
 59 #endif
 60 /* On FreeBSD 5, machine/ansi.h does not exist anymore... */
 61 #if defined (__FreeBSD__) && (__FreeBSD__ >= 5)
 62 #include <sys/_types.h>
 63 #endif

从注释看来,直觉上就是从FreeBSD 4过渡到5,换了个头文件,一切依旧似的。但看过这个<sys/_types.h>的代码后,很奇怪的事情被发现了。

sys/_types.h包含了<sys/cdefs.h>和<machine/_types.h>这两个header,以5.0.0版本FreeBSD来说,他们各自的源文件在(<machine/_types.h>我同样挑i386的):

http://svnweb.freebsd.org/base/release/5.0.0/sys/sys/_types.h?view=co

http://svnweb.freebsd.org/base/release/5.0.0/sys/sys/cdefs.h?view=co

http://svnweb.freebsd.org/base/release/5.0.0/sys/i386/include/_types.h?view=co

之所以说包含sys/_types.h很奇怪,是因为在上述三个文件里面,都找不到任何会被stddef.h依赖的东西,无论是宏还是类型定义。为了解答为什么要包含sys/_types.h,Mars在gcc社区上问了一下各路大牛,但得到的线索反而更支持前面的观点.......

其中从http://gcc.gnu.org/ml/gcc/2002-09/msg00560.html中,FreeBSD开发者Mike Barcroft阐述了从machine/ansi.h到sys/_types.h过渡是如何影响其他头文件对像size_t、ptrdiff_t这样的类型的定义。下面稍微翻译一下Mike邮件的重点,当然鼓励大伙原汁原味。同时也感谢gcc社区上的iant@google.com提供情报。

在FreeBSD < 5的时代,那些引用前者来定义foo_t类型的header采用的方法是,在<machine/ansi.h> 中: 

#define _BSD_FOO_T_ int;

在外部header中:

#include <machine/ansi.h>
...
#ifdef _BSD_FOO_T_
typedef _BSD_FOO_T_ foo_t;
#undef _BSD_FOO_T_
#endif

根据上面的代码,外部header对<machine/_types.h>的依赖就在于_BSD_FOO_T_宏上,因此包含他是必须的。

而在FreeBSD >= 5的时代,同样的事情变成这样,在<sys/_types.h>中:

typedef int __foo_t;

在外部header中:

#include <sys/_types.h> 
...
#ifndef _FOO_T_DECLARED
typedef __foo_t foo_t; 
#define _FOO_T_DECLARED
#endif

看起来好像外部header同样依赖于_FOO_T_DECLARED对吧,但事实并非如此,最直接的情况就是在<sys/_types.h>中完全没有这样的宏定义,因为这个宏的含义是foo_t类型已经被定义,而<sys/_types.h>从设计之处就不是做这个事情的角色,而是提供“元类型”给外部header来搞。那_BSD_FOO_T_呢?他不一样,这家伙就是元类型本身......

也就是说<sys/_types.h>的设计目标是对外部header承诺__foo_t这个元类型是必然存在的,要搞的人只要判断foo_t是不是已经定义就可以了(通过_FOO_T_DECLARED ),不必担心元类型存在与否。

那<stddef.h>是不是就因为需要元类型__foo_t,所以就包含<sys/_types.h>呢?这看起来很自然,然而真正的<stddef.h>实现中完全没能看到半个对__foo_t的依赖,他根本不用元类型来定义foo_t,从火车宏中可以看到,用的是__FOO_TYPE__预定义宏,天啊。

即使把<sys/_types.h>、<sys/cdefs.h>和<i386/include/_types.h>这三个header全身扒光,也无法找到任何一个被<stddef.h>依赖的元素,因此为何要有从<stddef.h>到<sys/_types.h>的包含关系,就成了此篇的一个未结之谜。Mars自己脑补了这么一个想法,还记得Sun OS中<sys/stdtypes.h>那家伙吧,也许<sys/_types.h>跟他一样是一个内部header,因此

其实都已经把上面那句话给敲出来了,但突然天雷滚滚,Linus上身,口吐白沫一下子出来了一个思路,当然也是多得<sys/stdtypes.h>了。

还记得Sun OS的那个<sys/stdtypes.h>是怎么搞到无数Solaris工程师一夜白首吗?就是因为那帮不听话的孩纸跳过了<sys/types.h>这个官方接口header,直接包含前者。马上查阅,确实在FreeBSD也有<sys/types.h>这个“标准的”系统接口header。

http://svnweb.freebsd.org/base/release/5.0.0/sys/sys/types.h?view=co

更重要的是,在里面有这么一段:

47 /* Machine type dependent parameters. */
48 #include <machine/endian.h>
49 #include <sys/_types.h>
......
242 #ifndef _SIZE_T_DECLARED
243 typedef __size_t        size_t;
244 #define _SIZE_T_DECLARED
245 #endif

这些证明了什么?果然<sys/_types.h>这厮是内部header!那跟前面<stddef.h>要包含他有虾米关系?当然有,Mars倒在座位上,闭上眼睛开始了他的推理。(这里有喜欢看柯南的木有?)

<stddef.h>作为glibc的一个标准类型定义头文件,无数人都把他当神一样拜——“包含他,得类型”,尽管在资料中他提供几种标准类型的定义(size_t、ptrdiff_t......),但搞这个<stddef.h>的那群野孩纸也许想让这个header能做得更多,于是就偷偷把手摸进了<sys/_types.h>的身体上,这样大量__foo_t元类型都被<stddef.h>给导出了,通过包含他就能做到更多利用元类型做的事情。

根据这样的推断,那在FreeBSD >= 5的OS中,似乎就隐含着这样一个事实——包含<stddef.h>就可以得到一系列元类型。事实上,到底是因为需要从<stddef.h>中导出更多元类型,才让他包含<sys/_types.h>;还是在开发过程中,大量的glibc代码因为通过<stddef.h>来依赖元类型,而不得不加上这样的“包含补丁”,这确实是一个迷,因为这种事情在大规模开发中发生,也确实是有可能的。

至此,在FreeBSD >= 5上对<stddef.h>包含<sys/_types.h>的一个结论是——为了给外部导出更多元类型。

 

===<stddef.h>的本意===

作为此篇的最后一小节,聊聊<stddef.h>事实上是在干什么。回顾前面提到的那些火车宏,可以看到一系列长长的宏开关检查,只要里面有一个宏开关已经打开,那就会放弃对foo_t类型的定义。

为什么要这样呢?从上一小节可以了解到,不同OS有各自定义标准类型的一套做法,差异大得可以让泰坦尼克号翻滚再翻滚,在这种现实环境下,<stddef.h>作为“标准类型定义的最后一道防线”而出现,他的目标通俗点说就是:“无论你们那帮开发OS接口的奇葩怎么乱来,通过我这里之后,必然有标准类型”。

这个目标就决定了<stddef.h>定义foo_t所使出的“终极武器”不能依赖于任何OS平台提供的元类型,而只能依赖gcc的接口,回顾前面size_t的火车宏:

#ifndef __SIZE_TYPE__
#define __SIZE_TYPE__ long unsigned int
#endif
#if !(defined (__GNUG__) && defined (size_t))
......

这里gcc提供的接口就是__SIZE_TYPE__这个预定义宏,那为什么还有一小段会自定义__SIZE_TYPE__?因为作为大家闺秀,<stddef.h>做得更加彻底:

122 /* In case nobody has defined these types, but we aren't running under
123    GCC 2.00, make sure that __PTRDIFF_TYPE__, __SIZE_TYPE__, and
124    __WCHAR_TYPE__ have reasonable values.  This can happen if the
125    parts of GCC is compiled by an older compiler, that actually
126    include gstddef.h, such as collect2.  */

注释真实帮了不少忙,这段说的就是,如果gcc 2.00以前的编译器编译<stddef.h>的话,就没有预定义的__FOO_TYPE__宏了。

好啦,终于dig完啦,不知道大伙满意否,反正自己还算挺满意,虽然也有美中不足的地方,希望将来可以补充回来(任何新的发现或者纠正都会更新的啦~)。此篇名曰《<stddef.h>一日游》,其实说的是打算用一天时间来写好(尽管前面已经花费了很多天来各种搜寻、证明信息),但最后还是写了好几天,能有这样的热情,也证明挖掘工程背后的方方面面真的非常有意思!!!说回来本来是在挖掘python的源码的,突然挖着挖着就来到gcc和glibc这边了,之后类似的事情估计还会发现,谁让python就是用c写的。

Mars擦了额头上的汗水,露出幸福的微笑扛起铲子跑回python源码的路上。

 

posted @ 2013-05-07 11:34  Miner Mars  阅读(1526)  评论(1编辑  收藏  举报