藏身于stdio.h中的size_t

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

                                                                                       ----火星人

  size_t 一直是c/c++频道的一线演员,他几乎出现在该频道的每一部连续剧当中,扮演着正义完美没有不良记录的角色。曾经有一段时间,Mars以为这位影星是从stddef.h出道的,不过当他在redhat和mac上写了一个简单的c程序,里面只包含一个stdio.h文件时,他发现size_t已经栖身其中。此篇就是dig一下size_t在用户程序崭露头角之前是怎么诞生的,此篇只讲述Mac OSX下的情况,戴红帽的那群兄弟,你们冷静点,本篇结尾会给你们交待的。

  既然要找出size_t的定义源头,那就少不了研究头文件的包含顺序。勤奋朴实的Mars在刚开始那些日子里采用的方法就是用肉眼和大脑去分析这个顺序,当然这是一个很复杂的过程,而且这样的出来的结果说服力不高,因为难保顺序根本不是Mars所推测的那样。

  某一天当Mars正为这个问题发愁的时候,他想起了gcc那拥有远古黑魔法的编译选项——"-E",于是他研究出来下面这套衍生命令:

gcc -E demo.cpp |  grep -P "^#" | grep -o "\".*\"" | uniq | less

demo.cpp是上面提到的程序(哎呀,就是那个只有#include<stdio.h>和空main的程序啦)。

这个命令干了啥不是此篇的重点,电视机前的观众或许已经心中有数,没数的自己去研究一下或者等Mars喝过可口的星巴克之后再书一篇关于这个的。

黑魔法炼金术施放之后出来的成品如下:

红色框框里面的不是死刑对象,虽然小学老师告诉Mars不能随便用红色,但麦当劳叔叔也说了,“我就喜欢!”。经过亲自分析之后,红框里面的5个header以及其顺序就是size_t诞生的过程,还等什么?马上进入实证的探寻过程!BOSS就在里面!

  在/usr/include/stdio.h中,有着size_t的直接定义:

 76 #ifndef _SIZE_T
 77 #define _SIZE_T
 78 typedef __darwin_size_t         size_t;
 79 #endif

而在下一步/usr/include/_types.h中,__darwin_size_t的身影消失得无影无踪,显然是躲到迷宫的下一层去了!(RPG迷“嗯”了一声,想起那曾经找过的BOSS)

  来到了/usr/include/sys/_types.h这一层还是晚了,但Mars看了一眼这一层的鬼斧神工,里面展示了无数构造系统的类型定义源头,他知道以后他还是会回来这里的。

  /usr/include/machine/_types.h只是一个分岔口,除去2/3的开场路牌注释,剩下的有效代码只有不够10行,没错,这个header的作用是用来决定具体机器平台的类型,然后才知道BOSS的真正所在,因为Mars的macbook pro是x86架构的,于是就leader着到最后一层。

 28 #ifndef _BSD_MACHINE__TYPES_H_
 29 #define _BSD_MACHINE__TYPES_H_
 30 
 31 #if defined (__i386__) || defined(__x86_64__)
 32 #include "i386/_types.h"
 33 #elif defined (__arm__)
 34 #include "arm/_types.h"
 35 #else
 36 #error architecture not supported
 37 #endif
 38 
 39 #endif /* _BSD_MACHINE__TYPES_H_ */

  在/usr/include/i386/_types.h中,找到了她,但却发现并不是他心中的那个她:

 89 #if defined(__GNUC__) && defined(__SIZE_TYPE__)
 90 #define MARS_ENTER_HERE 1
 91 typedef __SIZE_TYPE__           __darwin_size_t;        /* sizeof() */
 92 #else
 93 typedef unsigned long           __darwin_size_t;        /* sizeof() */
 94 #endif

通过自定义宏这招(不会不理解吧?),实证得知真正执行的定义代码是

typedef __SIZE_TYPE__    __darwin_size_t;

这一次RPG铁粉们实在不能忍了,这完全就是最终打败BOSS之后还留下续集的伏笔啊,什么__SIZE_TYPE__啊,银河系里又一大魔头吗!!Mars决定不等待续集的发布,直接在本集KO下集的BOSS,于是他又捡起了地上的宝剑......

  尽管在其他头文件里面乱找一通,但也没能看到__SIZE_TYPE__的踪影,不用怕,地球上还存在这样的咒语来检验真理的标准:

#if defined(__GNUC__)
#define MARS__GNUC__DEFINED 1
#else
#define MARS__GNUC__DEFINED 0
#endif

#if defined(__SIZE_TYPE__)
#define MARS__SIZE_TYPE__DEFINED 1
#else
#define MARS__SIZE_TYPE__DEFINED 0
#endif

#include <stdio.h>

注意#include <stdio.h>是在最下面的,在此之前已经没有任何header来干扰真理的检验。实证结果是在包含一切header之前,__GNUC__和__SIZE_TYPE__已经被定义了,他们真的是续集的BOSS!为了不引起地球人的暴动,还得找出更加官方的信息来支持这一说,于是Mars第一次游进了gcc的标准文档之海.......

  在gcc标准文档中打滚过的兄弟姐妹们都知道里面什么情况,捣腾了接近一个小时,下面这段文字终于被翻出来:

__SIZE_TYPE__

__PTRDIFF_TYPE__

__WCHAR_TYPE__

__WINT_TYPE__

__INTMAX_TYPE__

__UINTMAX_TYPE__

         These macros are defined to the correct underlying types for the size_t, ptrdiff_t, wchar_t, wint_t, intmax_t, and uintmax_t typedefs, respectively. They exist to make the standard header files stddef.h and wchar.h work correctly. You should not use these macros directly; instead, include the appropriate headers and use the typedefs.

文档位置是:http://gcc.gnu.org/onlinedocs/gcc-4.2.4/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros

  其实好多技术文章都会把官方文档中的英文亮一下以示高贵和镀金,但接下来都会跟着一段屌丝的翻译。这段鸡肠大概的意思就是这几个好基友macro是gcc用来表示机器级别的数值类型,然后这些数值类型又被stddef.h一翻烹调过后最终递到大伙面前,成为c标准下的那些大明星,其中之一就包括此篇的主角——size_t。

  好了,来到这里Mars已经超班BUG杀了小BOSS——__SIZE_TYPE__,最后Mars还是能找到size_t的真实身份——一个GCC预定义的数值类型,当然这个类型是可以在编译时修改的,但敢不敢改就另当别论了,毕竟他可是渗透全球的知名人物呢。但这只是在Mac OS X下的情况;redhat下的情况有点不一样,在那边涉及到上面提到的stddef.h,马上会有另一篇专门dig这位大家闺秀的背后。

posted @ 2013-05-06 11:57  Miner Mars  阅读(835)  评论(0编辑  收藏  举报