首页 > 编程 > .NET > 正文

C标准库源码解剖(1):类型相关的定义

2024-07-10 13:27:13
字体:
来源:转载
供稿:网友

说明:整个C标准库解剖系列环境为Ubuntu 8.04,编译器为gcc 4.2.4,由于linux系统中只有C标准库的头文件(在/usr/include下),函数库被编译成了程序库,没有源代码,因此对源代码的解剖用的是glibc 2.9,可从GNU的官方站点上下载。
    类型相关定义包括limits.h、float.h、stddef.h、stdbool.h、stdarg.h、iso646.h、stdint.h共7个头文件。除了stdint.h外,其余6个文件在gcc编译器的/usr/lib/gcc/i486-linux-gnu/4.2.4/include目录下。stdint.h在/usr/include中,是C99中引入的,提供了扩展整数的基本定义,放到后面再解剖吧。
    1、limits.h: 定义了整数类型的范围。/usr/include下也有limits.h,它会自己先定义各个整数类型范围,这样当不用gcc来构建你的程序时就可以使用这些值。如果使用gcc编译器来构建你的程序,则会使用gcc编译器自己的limits.h(前面的定义都会#undef掉)。由于这个limits.h会用到gcc内置的limits.h,因此我们解剖/usr/include下的limits.h。

 

[cpp] view plaincopy
  1. /* ISO C99 Standard: 7.10/5.2.4.2.1  整数类型的大小  <limits.h> */  
  2. #ifndef _LIBC_LIMITS_H_  
  3. #define _LIBC_LIMITS_H_ 1  
  4. #include <features.h> /* 选项的宏,如ISOC99选项、POSIX选项、XOPEN选项等 */  
  5. #define MB_LEN_MAX  16  /* 支持区域设置的多字节字符宽度为16位 */  
  6. /* 不使用GNU CC时就必须定义下面所有符号,否则使用gcc编译器中的定义(看下面) */  
  7. #if !defined __GNUC__ || __GNUC__ < 2   
  8. # ifndef _LIMITS_H  
  9. #  define _LIMITS_H 1  
  10. #include <bits/wordsize.h> /* 定义了表示字的位数的__WORDSIZE宏,64位平台上值为64,32位平台上值为32 */  
  11. #  define CHAR_BIT  8 /* char类型的宽度为8位 */  
  12. #  define SCHAR_MIN (-128)  /* signed char的最小值为-2^7,补码表示为10000000,没有对应的正数,其反数还是自己 */  
  13. #  define SCHAR_MAX 127     /* signed char的最大值为2^7-1=01111111 */  
  14. #  define UCHAR_MAX 255  /* unsigend char的最大值为2^8-1=11111111(最小值为0) */  
  15. #  ifdef __CHAR_UNSIGNED__  /* 根据预定义宏来确定是让char=unsigned char还是char=signed char */  
  16. #   define CHAR_MIN 0  
  17. #   define CHAR_MAX UCHAR_MAX  
  18. #  else  
  19. #   define CHAR_MIN SCHAR_MIN   /* gcc中使用了这个,即char=signed char */  
  20. #   define CHAR_MAX SCHAR_MAX  
  21. #  endif  
  22. #  define SHRT_MIN  (-32768) /* signed short int的最小值为-2^15 */  
  23. #  define SHRT_MAX  32767    /* signed short int的最小值为2^15-1 */  
  24. #  define USHRT_MAX 65535  /* unsigned short int的最大值为2^16-1(最小值为0) */  
  25. #  define INT_MIN   (-INT_MAX - 1) /* int的最小值为-2^31 */  
  26. #  define INT_MAX   2147483647  /* int的最大值为2^31-1 */  
  27. #  define UINT_MAX  4294967295U  /* unsigned int的最大值为2^32-1(最小值为0) */  
  28. #  if __WORDSIZE == 64  /* 64位的x86平台,这个宏在<bits/wordsize.h>中 */  
  29. #   define LONG_MAX 9223372036854775807L  /* signed long int最大值为2^63-1 */  
  30. #  else             /* 32位的x86平台 */  
  31. #   define LONG_MAX 2147483647L  /* signed long int最大值为2^31-1 */  
  32. #  endif  
  33. #  define LONG_MIN  (-LONG_MAX - 1L)  /* signed long int最小值为-2^31 */  
  34. #  if __WORDSIZE == 64    
  35. #   define ULONG_MAX    18446744073709551615UL /* 64平台:unsigend long int最大值为2^64-1 */  
  36. #  else  
  37. #   define ULONG_MAX    4294967295UL /* 32位平台:unsigned long int最大值为2^32-1 */  
  38. #  endif  
  39. #  ifdef __USE_ISOC99  /* <feature.h>中宏:long long类型为C99标准引入的 */  
  40. #   define LLONG_MAX    9223372036854775807LL  /* signed long long int最大值为2^63-1 */  
  41. #   define LLONG_MIN    (-LLONG_MAX - 1LL)  /* signed long long int最小值为-2^63 */  
  42. #   define ULLONG_MAX   18446744073709551615ULL /* unsigned long long int最大值为2^64-1 */  
  43. #  endif /* ISO C99 */  
  44. # endif /* limits.h  */  
  45. #endif  /* GCC 2.  */  
  46. #endif  /* !_LIBC_LIMITS_H_ */  
  47. /* 获取编译器的limits.h,其中定义几乎所有的ISO常量 */  
  48. #if defined __GNUC__ && !defined _GCC_LIMITS_H_  /* _GCC_LIMITS_H_是GCC的文件定义  */  
  49. # include_next <limits.h>  
  50. #endif  
  51. /* 一些gcc版本的<limits.h>没有定义LLONG_MIN、LLONG_MAX和ULLONG_MAX,则这里需要进行定义 */  
  52. #if defined __USE_ISOC99 && defined __GNUC__  
  53. # ifndef LLONG_MIN  
  54. #  define LLONG_MIN (-LLONG_MAX-1)  
  55. # endif  
  56. # ifndef LLONG_MAX  
  57. #  define LLONG_MAX __LONG_LONG_MAX__  
  58. # endif  
  59. # ifndef ULLONG_MAX  
  60. #  define ULLONG_MAX    (LLONG_MAX * 2ULL + 1)  
  61. # endif  
  62. #endif  
  63. #ifdef  __USE_POSIX  
  64. /* POSIX添加东西到<limits.h>中 */  
  65. # include <bits/posix1_lim.h>  
  66. #endif  
  67. #ifdef  __USE_POSIX2  
  68. # include <bits/posix2_lim.h>  
  69. #endif  
  70. #ifdef  __USE_XOPEN  
  71. # include <bits/xopen_lim.h>  
  72. #endif  

 

    解释:
    (1)/usr/include/limits.h的实现中,char=unsigned char占8位,short占16位,int占32位,long在64位平台上占64位,在32位平台上占32位,C99标准引入的long long占64位。它们都有singed和unsigned两种,默认都是带符号整数(signed)。带符号整数在当前大多数体系结构上一般都用二进制补码表示(当然C标准也支持用其他一些编码表示),即正数用直接编码,符号位为0;负数表示为对应正数各位取反然后加1,符号位为1。带符号整数范围为-2**(n-1)~2**(n-1)-1,其中最小负数-2**(n-1)=100...0没有对应正数,其反数还是自己。无符号整数用直接二进制编码,范围为0~2**n-1。如果使用gcc的limits.h,则每个宏的值依赖于gcc编译器内置的定义,一般跟这里的值一致。
    (2)UCHAR_MAX必须等于2**CHAR_BIT-1,且对带符号整数一般有MIN=-MAX-1。
    (3)feature.h文件中定义了一些表示编译选项的宏,如ISOC99选项、POSIX选项、XOPEN选项等。bits/wordsize.h定义了表示字的位数的__WORDSIZE宏,64位平台上值为64,32位平台上值为32。它们都在/usr/include下。
    (4)如果要新遵循C99标准,则有些gcc版本的<limits.h>可能没有定义LLONG_MIN、LLONG_MAX和ULLONG_MAX,则这里需要进行定义。如果使用POSIX标准,则还要添加一些POSIX中的东西。
    2、float.h: 定义了浮点数类型的特征。

 

[cpp] view plaincopy
  1. /* ISO C Standard: 5.2.4.2.2  浮点数类型的特征  <float.h> */  
  2. #ifndef _FLOAT_H___  
  3. #define _FLOAT_H___  
  4. /* 实数浮点数表示: 
  5.    x=s*(b**e)*[f1*b**(-1)+f2*b**(-2)+...+fp*b**(-p)], emin<=e<=emax,(**表示求幂) 
  6.    s  是符号(+1或-1) 
  7.    b  是进制基数(通常为2、8、16) 
  8.    e  是指数值,取值范围在emin与emax之间 
  9.    p  是b进制的有效位数 
  10.    fk  是有效数字,0<=fk<b 
  11.  */  
  12.    
  13. /* b:指数表示法的基数 */  
  14. #undef FLT_RADIX  
  15. #define FLT_RADIX   __FLT_RADIX__  /* 进制基数b,一般为2,适用于所有三种浮点类型 */  
  16. /* p:是b进制的有效位数  */  
  17. #undef FLT_MANT_DIG  
  18. #undef DBL_MANT_DIG  
  19. #undef LDBL_MANT_DIG  
  20. #define FLT_MANT_DIG    __FLT_MANT_DIG__   /* float的b进制有效位数p */  
  21. #define DBL_MANT_DIG    __DBL_MANT_DIG__   /* double的b进制有效位数p */  
  22. #define LDBL_MANT_DIG   __LDBL_MANT_DIG__  /* long double的b进制有效位数p */  
  23. /* q:精度小数位数。使任何有q个小数位的浮点数能被舍入成有p个b进制位数的浮点数,并且不需要改变这q个小数位 
  24.     就可以重新转换回来。q的值为: 
  25.     p * log10(b)            如果b是10的幂 
  26.     floor((p - 1) * log10(b))   否则 
  27. */  
  28. #undef FLT_DIG  
  29. #undef DBL_DIG  
  30. #undef LDBL_DIG  
  31. #define FLT_DIG     __FLT_DIG__  /* float的精度小数位数,通常为6 */  
  32. #define DBL_DIG     __DBL_DIG__  /* double的精度小数位数,通常为10 */  
  33. #define LDBL_DIG    __LDBL_DIG__  /* long double的精度小数位数,通常为10 */  
  34. /* emin:最小负整数x,使b**(x-1)在规格化浮点数类型取值范围内 */  
  35. #undef FLT_MIN_EXP  
  36. #undef DBL_MIN_EXP  
  37. #undef LDBL_MIN_EXP  
  38. #define FLT_MIN_EXP __FLT_MIN_EXP__  /* float的emin */  
  39. #define DBL_MIN_EXP __DBL_MIN_EXP__   /* double的emin */  
  40. #define LDBL_MIN_EXP    __LDBL_MIN_EXP__   /* long double的emin */  
  41. /* ceil(log10(b)*(emin-1)):最小负整数x,使10**(x-1)在 
  42.     规格化浮点数类型取值范围内,通常值为-37 
  43. */  
  44. #undef FLT_MIN_10_EXP  
  45. #undef DBL_MIN_10_EXP  
  46. #undef LDBL_MIN_10_EXP  
  47. #define FLT_MIN_10_EXP  __FLT_MIN_10_EXP__  
  48. #define DBL_MIN_10_EXP  __DBL_MIN_10_EXP__  
  49. #define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__  
  50. /* emax:最大整数x,使b**(x-1)在可表示的有限浮点数取值范围内 */  
  51. #undef FLT_MAX_EXP  
  52. #undef DBL_MAX_EXP  
  53. #undef LDBL_MAX_EXP  
  54. #define FLT_MAX_EXP __FLT_MAX_EXP__  /* float的emax */  
  55. #define DBL_MAX_EXP __DBL_MAX_EXP__  /* double的emax */  
  56. #define LDBL_MAX_EXP    __LDBL_MAX_EXP__  /* long double的emax */  
  57. /* floor(log10((1 - b**-p) * b**emax)):最大整数x,使10**x在可 
  58.     表示的有限浮点数取值范围内,通常值为37 
  59. */  
  60. #undef FLT_MAX_10_EXP  
  61. #undef DBL_MAX_10_EXP  
  62. #undef LDBL_MAX_10_EXP  
  63. #define FLT_MAX_10_EXP  __FLT_MAX_10_EXP__  
  64. #define DBL_MAX_10_EXP  __DBL_MAX_10_EXP__  
  65. #define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__  
  66. /* (1-b**-p)*b**emax:可表示的最大有限浮点数, 通常值为10**37 */  
  67. #undef FLT_MAX  
  68. #undef DBL_MAX  
  69. #undef LDBL_MAX  
  70. #define FLT_MAX     __FLT_MAX__  
  71. #define DBL_MAX     __DBL_MAX__  
  72. #define LDBL_MAX    __LDBL_MAX__  
  73. /* b**(1-p):最小的x(x>0.0),使1.0+x>1.0,宏定义为允许的最大值,如对 
  74.    float为10**-5,对double和long double为10**-9  */  
  75. #undef FLT_EPSILON  
  76. #undef DBL_EPSILON  
  77. #undef LDBL_EPSILON  
  78. #define FLT_EPSILON __FLT_EPSILON__  
  79. #define DBL_EPSILON __DBL_EPSILON__  
  80. #define LDBL_EPSILON    __LDBL_EPSILON__  
  81. /* b**(emin - 1):最小规格化正数,通常值为10**-37  */  
  82. #undef FLT_MIN  
  83. #undef DBL_MIN  
  84. #undef LDBL_MIN  
  85. #define FLT_MIN     __FLT_MIN__  
  86. #define DBL_MIN     __DBL_MIN__  
  87. #define LDBL_MIN    __LDBL_MIN__  
  88. /* 舍入方式:0:向0舍入;1:最近舍入;2:向正无穷大舍入;3:向负无穷d大舍入;-1:不确定, 
  89.     适用于所有3种浮点类型 */  
  90. #undef FLT_ROUNDS  
  91. #define FLT_ROUNDS 1 /* 使用最近舍入方式 */  
  92. #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L  
  93. /* 浮点表达式求值方法(C99中引入): 
  94.    -1  不确定 
  95.     0  求值时仍保持原类型的精度与取值范围 
  96.     1  求值时float类型与double类型统一用double类型;long double类型保持不变 
  97.     2  求值时全部用long double类型 
  98. */  
  99. #undef FLT_EVAL_METHOD  
  100. #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__  
  101. /* n(C99中引入):小数位数,使任何有pmax个b进制数位的最宽浮点数类型能被舍入 
  102.     成有n个小数位的浮点数,并且无需改变这个值就可以重新转换回来。n的值为: 
  103.     pmax * log10(b)         如果b是10的幂 
  104.     ceil(1 + pmax * log10(b))   否则 
  105. */  
  106. #undef DECIMAL_DIG  
  107. #define DECIMAL_DIG __DECIMAL_DIG__  /* 通常的值为10 */  
  108. #endif /* C99 */  
  109. #endif /* _FLOAT_H___ */  

 

    解释:
    (1)浮点数的形式为x=s*(b**e)*[f1*b**(-1)+f2*b**(-2)+...+fp*b**(-p)], emin<=e<=emax(**表示求幂)。s是符号位,b是进制基数,e是指数值,p是b进制的有效位数,0<=fk<b。
    (2)IEEE的浮点数表示法:单精度float型有1位符号位S,8位指数E,23位尾数M。转换成数值V=(-1)**S*1.M*2**(E-127)。例如16.5=00010000.1=1.00001*2**4(成为规格化数),则符号位为0,指数位为4+127=131=10000011(因为指数可以为负,8位有符号数的范围为-128~127,为了统一用无符号数表示,要加上127),尾数为00001000000000000000000,拼接起来即得到16.5的内存表示01000001100001000000000000000000。
    (3)常用的宏有FLT_DIG/DBL_DIG/LDBL_DIG、FLT_MIN/DBL_MIN/LDBL_MIN、FLT_MAX/DBL_MAX/LDBL_MAX。
    3、stddef.h: 定义了ptrdiff_t、size_t、wchar_t、wint_t类型和offsetof。有一大堆兼容不同平台的条件编译宏,这对我们没什么用,略去。

 

[cpp] view plaincopy
  1. /* ISO C Standard: 7.17  一些通用定义  <stddef.h> */  
  2. typedef __PTRDIFF_TYPE__ ptrdiff_t;  /* 定义带符号整数类型ptrdiff_t,这里__PTRDIFF_TYPE__ 
  3.                                                   依赖于平台,通常为long类型 */  
  4. #if !(defined (__GNUG__) && defined (size_t))  
  5. typedef __SIZE_TYPE__ size_t/* 定义无符号整型size_t,__SIZE_TYPE__ 
  6.                                          依赖于平台,通常为unsigned int或unsigned long类型 */  
  7. #ifdef __BEOS__  
  8. typedef long ssize_t;  /* 只用于BeOS系统中 */  
  9. #endif   
  10. #endif  
  11. typedef _BSD_RUNE_T_ rune_t; /* 只用于老的BSD系统 */  
  12. #ifndef _RUNE_T_DECLARED  
  13. typedef __rune_t rune_t;  /* 只用于FreeBSD系统中 */  
  14. #define _RUNE_T_DECLARED  
  15. #endif  
  16. #ifndef __WCHAR_TYPE__  
  17. #define __WCHAR_TYPE__ int  
  18. #endif  
  19. #ifndef __cplusplus  /* C中才要定义wchar_t,C++中wchar_t为内置类型 */  
  20. typedef __WCHAR_TYPE__ wchar_t/* 宽字符类型wchar_t也在stddef.h中定义,这里为int类型 */  
  21. #endif  
  22. #ifndef __WINT_TYPE__  
  23. #define __WINT_TYPE__ unsigned int  
  24. #endif  
  25. typedef __WINT_TYPE__ wint_t;   /* 用于无符号的宽字符类型中 */  
  26. #ifndef __cplusplus  
  27. #define NULL ((void *)0)  /* C中定义NULL指针常量为(void*)0 */  
  28. #else    
  29. #define NULL 0   /* C++中定义NULL指针常量为0 */  
  30. #endif  
  31. #ifdef _STDDEF_H  
  32. /* 结构成员的地址偏移字节数,TYPE结构类型,MEMBER为其某个成员 */  
  33. #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)  
  34. #endif  

 

    ptrdiff_t是两个指针相减所得的带符号整型,一般用long类型表示。size_t是sizeof运算得到的无符号整型,一般用unsigned int或unsigned long表示。宽字符类型wchar_t也在stddef.h中定义,这里为int类型。wint_t用于无符号的宽字符类型中,这里为unsigned int类型。offsetof宏用于计算结构成员的地址偏移字节数。       
    4、stdbool.h: 是C99中增加的,定义了布尔类型bool,及其两个常量false=0、true=1。__bool_true_false_are_defined=1是标识布尔类型定义是否完成的信号。这些定义与C++中的一致,因此标准C++并不需要另外再支持stdbool.h,但GCC提供了这个扩展,使得在C++中可以支持<stdbool.h>。

 

[cpp] view plaincopy
  1. /* ISO C Standard:  7.16  布尔类型及其值  <stdbool.h> 
  2.  */  
  3. #ifndef _STDBOOL_H  
  4. #define _STDBOOL_H  
  5. #ifndef __cplusplus    
  6. #define bool    _Bool  /* C中 */  
  7. #define true    1  
  8. #define false   0  
  9. #else /* C++中 */  
  10. /* 在C++中支持<stdbool.h>是GCC的一个扩展 */  
  11. #define _Bool   bool  
  12. #define bool    bool  
  13. #define false   false  
  14. #define true    true  
  15. #endif /* __cplusplus */  
  16. /* 标识布尔类型定义是否完成的信号  */  
  17. #define __bool_true_false_are_defined   1  
  18. #endif  /* stdbool.h */  

 

    5、stdarg.h: 访问可变参数表的类型和函数(用宏实现)。当你需要编写有可变参数表的函数时,比如myfunc(int *a,...),你就可以用stdarg.h中的各个函数来遍历“...”中的各个实参,以完成该函数的功能。略去没有用的一大堆用于兼容不同平台的条件编译宏,如下:
   

[cpp] view plaincopy
  1. /* ISO C Standard:  7.15  可变参数表  <stdarg.h> */  
  2. #ifndef __GNUC_VA_LIST  
  3. #define __GNUC_VA_LIST  
  4. typedef __builtin_va_list __gnuc_va_list; /* 定义__gnuc_va_list.  */  
  5. #endif  
  6. #ifdef _STDARG_H  
  7. /* var_start(v,l):初始化用于遍历参数表的状态变量v */  
  8. #define va_start(v,l)   __builtin_va_start(v,l)  
  9. /* 结束参数表的遍历,对v和参数表做必要的整理操作 */  
  10. #define va_end(v)   __builtin_va_end(v)  
  11. /* 返回v当前指向的参数值 */  
  12. #define va_arg(v,l) __builtin_va_arg(v,l)  
  13. #if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L  
  14. /* 本函数C99中引入:将s复制到d中,生成指向当前参数的第二个指针 */  
  15. #define va_copy(d,s)    __builtin_va_copy(d,s)  
  16. #endif  
  17. #define __va_copy(d,s)  __builtin_va_copy(d,s)  
  18. typedef __gnuc_va_list va_list/* 定义va_list类型,用这种类型来定义遍历可变 
  19.                                             参数表的状态变量 */  
  20.                                               
  21. #endif /* _STDARG_H */                                              

    解释:
    (1)va_list类型:用这种类型来定义遍历可变参数列表的状态变量ap。
    (2)va_start(ap,lt):让ap的内部指针指向第一个可变参数。需要用lt来指定可变参数表前面的最后一个固定参数。遍历开始必须先调用这个函数。
    (3)var_arg(ap,type):获取当前ap内部指针指向的参数值,然后把指针移动下一个参数,下一个参数的类型要用type指定。
    (4)va_end(ap):完成对可变参数表的遍历,会对ap和参数表作必要的整理工作。遍历结束时必须要调用这个函数。
    (5)va_copy(dest,src):c99中引入,将src复制到dest中,dest和src均为va_list型状态变量。这样就生成指向当前参数的第二个状态变量,然后可以独立的使用src和dest来遍历可变参数表。dest中也要像src中一样调用va_end。
    6、iso646.h: 为逻辑运算符定义一些方便使用的宏,是C89增补1中增加的。

 

 

[cpp] view plaincopy
  1. /* ISO C Standard:  7.9  一些运算符宏  <iso646.h> */  
  2. #ifndef _ISO646_H  
  3. #define _ISO646_H  
  4. #ifndef __cplusplus  
  5. #define and &&  
  6. #define and_eq  &=  
  7. #define bitand  &  
  8. #define bitor   |  
  9. #define compl   ~  
  10. #define not !  
  11. #define not_eq  !=  
  12. #define or  ||  
  13. #define or_eq   |=  
  14. #define xor ^  
  15. #define xor_eq  ^=  
  16. #endif  
  17. #endif  

 

    之所以要为&&、|、!、^等这些运算符定义一个宏,是因为在ISO 646的字符集中要使用这些特殊的符号可能不方便,而用等价的宏名and、bitor、not、xor就比较方便了,在C++中这些宏名是关键字。C89增补1还提供了一些能在ISO 646中方便使用的字符来拼写{、}之类的符号。如<%、%>、<:、:>、%:、%:%分别等价于字符{、}、[、]、#、##。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表