"没有升迁发财,没有大牛小白,挖掘工程的背后是回归最初兴趣的源动力。“
----火星人
===<stddef.h>简介===
<stddef.h>不算是旅游胜地,但在著名旅游指南cplusplus.com上搜索一把还是能看到相应的介绍:
此篇就是dig一下gcc下这位大家闺秀的方方面面,找点乐子(被那些无聊的工作闷得受不了的Mars点了一下头)。
看看需要stddef.h的男士很多,随便来个
grep "#include" -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 definitions30 */ 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 casehas defined it. */175 #ifndef _SYS_SIZE_T_H176 #ifndef _T_SIZE_177 #ifndef _T_SIZE178 #ifndef __SIZE_T179 #ifndef _SIZE_T_180 #ifndef _BSD_SIZE_T_181 #ifndef _SIZE_T_DEFINED_182 #ifndef _SIZE_T_DEFINED183 #ifndef _BSD_SIZE_T_DEFINED_ /* Darwin */184 #ifndef _SIZE_T_DECLARED /* FreeBSD 5 */185 #ifndef ___int_size_t_h186 #ifndef _GCC_SIZE_T187 #ifndef _SIZET_188 #ifndef __size_t189 #define __size_t__ /* BeOS */190 #define __SIZE_T__ /* Cray Unicos/Mk */191 #define _SIZE_T192 #define _SYS_SIZE_T_H193 #define _T_SIZE_194 #define _T_SIZE195 #define __SIZE_T196 #define _SIZE_T_197 #define _BSD_SIZE_T_198 #define _SIZE_T_DEFINED_199 #define _SIZE_T_DEFINED200 #define _BSD_SIZE_T_DEFINED_ /* Darwin */201 #define _SIZE_T_DECLARED /* FreeBSD 5 */202 #define ___int_size_t_h203 #define _GCC_SIZE_T204 #define _SIZET_205 #if defined (__FreeBSD__) && (__FreeBSD__ >= 5)206 /* __size_t is a typedef on FreeBSD 5!, must not trash it. */207 #else208 #define __size_t209 #endif210 #ifndef __SIZE_TYPE__211 #define __SIZE_TYPE__ long unsigned int212 #endif213 #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_h52 /* 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上能找到的大抵有以下两种:
其他的一些采用的甚至不是__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_UNIXtypedef 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 */#endiftypedef 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_UNIXtypedef long clock_t; /* units=ticks (typically 60/sec) */typedef long time_t; /* value = secs since epoch */typedef int size_t; /* ??? */#endiftypedef 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 #include59 #endif
这个兼容主要是针对旧OS,就FreeBSD来说就是5之前的版本。下面我们就拿FreeBSD 4.3的i386/include/ansi.h来挖一下有什么有趣的玩意。下面是相应的源码地址:
不是<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 #include59 #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_T272 #define _BSD_WCHAR_T_278 #define _GCC_WCHAR_T322 #ifndef __WCHAR_TYPE__323 #define __WCHAR_TYPE__ int324 #endif326 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 #include59 #endif 60 /* On FreeBSD 5, machine/ansi.h does not exist anymore... */ 61 #if defined (__FreeBSD__) && (__FreeBSD__ >= 5) 62 #include 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的):
之所以说包含sys/_types.h很奇怪,是因为在上述三个文件里面,都找不到任何会被stddef.h依赖的东西,无论是宏还是类型定义。为了解答为什么要包含sys/_types.h,Mars在gcc社区上问了一下各路大牛,但得到的线索反而更支持前面的观点.......
其中从中,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...#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...#ifndef _FOO_T_DECLAREDtypedef __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。
更重要的是,在里面有这么一段:
47 /* Machine type dependent parameters. */48 #include49 #include ......242 #ifndef _SIZE_T_DECLARED243 typedef __size_t size_t;244 #define _SIZE_T_DECLARED245 #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 under123 GCC 2.00, make sure that __PTRDIFF_TYPE__, __SIZE_TYPE__, and124 __WCHAR_TYPE__ have reasonable values. This can happen if the125 parts of GCC is compiled by an older compiler, that actually126 include gstddef.h, such as collect2. */
注释真实帮了不少忙,这段说的就是,如果gcc 2.00以前的编译器编译<stddef.h>的话,就没有预定义的__FOO_TYPE__宏了。
好啦,终于dig完啦,不知道大伙满意否,反正自己还算挺满意,虽然也有美中不足的地方,希望将来可以补充回来(任何新的发现或者纠正都会更新的啦~)。此篇名曰《<stddef.h>一日游》,其实说的是打算用一天时间来写好(尽管前面已经花费了很多天来各种搜寻、证明信息),但最后还是写了好几天,能有这样的热情,也证明挖掘工程背后的方方面面真的非常有意思!!!说回来本来是在挖掘python的源码的,突然挖着挖着就来到gcc和glibc这边了,之后类似的事情估计还会发现,谁让python就是用c写的。
Mars擦了额头上的汗水,露出幸福的微笑扛起铲子跑回python源码的路上。