<stddef.h>一日游 た 入场券 2022-01-10 13:07 178阅读 0赞 *"没有升迁发财,没有大牛小白,挖掘工程的背后是回归最初兴趣的源动力。“* *----火星人* **===<stddef.h>简介===** <stddef.h>不算是旅游胜地,但在著名旅游指南cplusplus.com上搜索一把还是能看到相应的介绍: ![07102832-2c1f0a0cc0274e608d353496f58f5770.png][] 此篇就是dig一下gcc下这位大家闺秀的方方面面,找点乐子(被那些无聊的工作闷得受不了的Mars点了一下头)。 看看需要stddef.h的男士很多,随便来个 grep "#include <stddef.h>" -r /usr/include 就保证可以找到一堆: ![07103011-ab82ed36ed364af9a47da55203485932.png][] <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"会有很有趣的事情发生…… ![07104017-4ad0cae0694b4119b3713f64feab4b0b.png][] 这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][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][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][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_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_sys_cdefs.h_view_co] [http://svnweb.freebsd.org/base/release/5.0.0/sys/i386/include/\_types.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][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][http_svnweb.freebsd.org_base_release_5.0.0_sys_sys_types.h_view_co 1] 更重要的是,在里面有这么一段: 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源码的路上。 转载于:https://www.cnblogs.com/mmars/archive/2013/05/07/3064323.html [07102832-2c1f0a0cc0274e608d353496f58f5770.png]: /images/20220110/71a73391237a40a3b2491f6d4de542a1.png [07103011-ab82ed36ed364af9a47da55203485932.png]: /images/20220110/5ae96341eff74a2cb57631f8804b72d1.png [07104017-4ad0cae0694b4119b3713f64feab4b0b.png]: /images/20220110/2bf1bb1e8b6a455fa3d342ca3128c2d1.png [ftp_pds.nasa.gov_pub_toplevel_tools_other_tools_retire_Solaris_source_stdtypes.h]: http://ftp//pds.nasa.gov/pub/toplevel/tools/other_tools_retire/Solaris/source/stdtypes.h [http_stuff.mit.edu_afs_sipb_project_gnat_src_gnat-3.05_src_ada_threads_include_pthread_stdtypes.h]: http://stuff.mit.edu/afs/sipb/project/gnat/src/gnat-3.05/src/ada/threads/include/pthread/stdtypes.h [http_svnweb.freebsd.org_base_release_4.3.0_sys_i386_include_ansi.h_view_co]: http://svnweb.freebsd.org/base/release/4.3.0/sys/i386/include/ansi.h?view=co [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/_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/sys/cdefs.h?view=co [http_svnweb.freebsd.org_base_release_5.0.0_sys_i386_include_types.h_view_co]: http://svnweb.freebsd.org/base/release/5.0.0/sys/i386/include/_types.h?view=co [http_gcc.gnu.org_ml_gcc_2002-09_msg00560.html]: http://gcc.gnu.org/ml/gcc/2002-09/msg00560.html [http_svnweb.freebsd.org_base_release_5.0.0_sys_sys_types.h_view_co 1]: http://svnweb.freebsd.org/base/release/5.0.0/sys/sys/types.h?view=co
相关 记某非法站点后台一日游 记某非法站点后台一日游 最近朋友a的一个朋友可能遇到了类似的黑灰产,导致我的朋友a被无辜误伤,才觉这些黑灰产业的触角已经无处不在。很久没写博客了,正好最近看到一个敲诈站点 本是古典 何须时尚/ 2022年10月16日 04:53/ 0 赞/ 141 阅读
相关 凤凰岭一日游 作者:朱金灿 来源:[http://blog.csdn.net/clever101][http_blog.csdn.net_clever101] 上周六 太过爱你忘了你带给我的痛/ 2022年08月05日 14:27/ 0 赞/ 113 阅读
相关 成都一日游 作者:朱金灿 来源:[http://blog.csdn.net/clever101][http_blog.csdn.net_clever101] 前段 柔情只为你懂/ 2022年07月26日 01:43/ 0 赞/ 140 阅读
相关 Git常用命令一日游活动 写在前面 如果只解释命令的用法的话,我想,是非常枯燥,而且没人愿意去看,看了也学不会,学不会就用不了,用不了就…就没有然后了,所以,我准备模拟一个项目的建立和完整的流程, r囧r小猫/ 2022年06月07日 10:39/ 0 赞/ 171 阅读
相关 黄龙溪一日游——传智播客成都java培训中心10月15日基础班 11月!天气渐渐转凉!但是传智播客成都java培训中心学员们的热情还没有褪去! 为了缓解学员们学习的压力,为了加深学员们之间的感情,促进学员们彼此之间的交流,传智播客组 今天药忘吃喽~/ 2022年05月16日 06:27/ 0 赞/ 171 阅读
相关 一日游 "没有升迁发财,没有大牛小白,挖掘工程的背后是回归最初兴趣的源动力。“ ----火星人 ===<stddef.h>简介=== <stddef.h>不算是旅游胜地,但在著 た 入场券/ 2022年01月10日 13:07/ 0 赞/ 179 阅读
还没有评论,来说两句吧...