C语言国际标准 ISO/IEC 9899:1999(E) 翻译(四):库
By RiverTime
库
7.1.2 标准头文件
1 声明的每一个库函数, 包括其原型, 头文件。【注153:头文件不必是一个源文件,头文件名字中在'< ' 和 '>' 之间的字符序列也不必是合法的源文件名】 头文件的内容通过 #include 预处理指示提供。头文件声明了一个相关函数集,必要的类型和宏定义。在此条款中描述的声明类型不应当包括类型限定,除非在别的地方明确规定。
2 标准头文件如下:
<assert.h>
<complex.h>
<ctype.h>
<errno.h>
<fenv.h>
<float.h>
<inttypes.h>
<iso646.h>
<limits.h>
<locale.h>
<math.h>
<setjmp.h>
<signal.h>
<stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdio.h>
<stdlib.h>
<string.h>
<tgmath.h>
<time.h>
<wchar.h>
<wctype.h>
3 如果一个文件与上面提到的'<' 和 '>' 界定序列同名,不作为实现的一部分提供,被放在用来搜索已包含的源文件的任何标准的地方,此行为不被定义。
4 标准头文件可以以任何顺序包含;在给定的范围内每个头文件可以不止一次被包含,与仅包含一次的效果没有什么不同,但是包含依赖于NDEBUG 定义的<assert.h> 文件是例外(请看 7.2)。头文件应当在任何外部声明或定义之外被包含,也应当在它所声明的任何函数或对象,所定义的任何类型或宏的第一次被引用之前被包含。然而,如果标识符在多于一个头文件中声明或定义,第二及以后的相关头文件允许在标识符的初始引用之后被包含。程序不应当有任何宏名字和包含之前定义的关键字相同。
5 任何在此条款中描述的与对象相似的宏的定义应当展开成被圆括号完全保护的代码,这样它在任意一个表达式中聚合,就好像它是一个单一的标识符。
6 库函数的任何声明应当有相应的外部链接。
7 在附录B中给出了标准头文件内容概要。进一步的参考请看:诊断(7.2)。
7.1.3 保留标识符
1 每个头文件声明或定义了在其相关的子条款中列出的所有标识符,同时有选择地声明或定义了在其相关的将来库方向子条款中列出的所有标识符,以及那些要么是为其他任何用处要么是为文件范围内标识符而一直保留的标识符。
——所有以下划线和紧接着一个大写字母或另一个下划线开始的标识符总是保留。
——所有以下划线开始的标识符总是保留,用于普通和标签名字空间的文件作用域标识符。
——在任何以下子条款(包括将来库方向)中的每一宏名字保留用来指定其相关的任何头文件是否被包含;除非另外明确规定(请看 7.1.4)。
——所有在以下的任何一个子条款(包括将来库方向)中有外部链接的标识符总是保留,用于作为外部链接的标识符。
【注 154: 外部链接的保留标识符包括 error,setjmp 和va_end。】
——在以下列出的任何子条款(包括将来库方向)中列出的每一个文件作用域标识符保留,用于宏名字,同时也用于在同一名字空间中的文件作用域标识符,如果其相应的任何头文件被包含的话。
2 其它的标识符不被保留。 如果程序声明或定义一个在某个上下文中被保留的标识符(不同于随后的7.1.4),或者定义一个保留标识符为一个宏名字, 其行为不定义。
3 如果一个程序删除(通过 #undef)了以上所列的标识符的任何宏定义, 其行为不定义。
7.1.4 库函数的使用
1 以下陈述的每一点都适用,除非另外有如下所详细描述的明确声明:如果传给函数的参数值不合法(例如,值在函数的作用域之外,或者指针在程序的地址空间之外,或者空指针,或者指针指向不可写内存而对应的参数不限定为常量 ),或者一个类型(向上转换之后)不符合带可变参数数目函数的定义,此行为不定义。如果函数参数描述为一个数组,则实际传给函数的指针应当是一个地址计算和访问对象都真正合法的值(如果该指针真的指向数组的第一个元素,则其是合法的)。在头文件中定义的任何函数,可能会以在头文件中定义的函数样的宏再次实现,所以如果库函数通过包含头文件被明确声明,以下技术之一可用于保证声明不受这样的宏的影响。函数的任何宏定义可通过用圆括号括住函数名的办法被局部禁止,因为此时名字后面不再跟着指示宏函数名展开的左圆括号。同样,允许获得库函数的地址即使其也是定义为一个宏。【注 155】 用 #undef 屏蔽宏定义同时也确保了实际函数的引用。用宏实现的库函数的任何调用应当展开为每个参数只算一次的代码,而且在需要时用圆括号保护,所以使用任意表达式作为参数通常是安全的【注 156】。同样,那些在以下子条款中描述的函数样的宏可以在表达式中调用,无论在何处带有一致返回类型的函数可以被调用【注 157】。所有列出的展开成整型常量表达式对象这样的宏应当在 #if 预处理指示中适用。
2 如果库函数声明时不引用在某个头文件里定义的类型,则允许声明并使用该函数时不包含其对应的头文件。
3 在库函数返回之前,有一顺序点。
4 不担保标准库的函数是可重入的,它可能修改静态存储域里的对象。 【注158】
【注 155】这意味着一个实现应当为每一个库函数提供一个实际的函数,尽管它
也为该函数提供一个宏。
【注 156】这样的宏可能不包括顺序点,而其对应的函数调用包括。
【注 157】 因为以下划线开始的外部标识符和一些宏名字是保留的,实现可以
为这些名字提供特殊的语义。例如,标识符_BUILTIN_abs 可用于指示abs 函数
内联码的产生。所以, 适当的头文件可以为编译器指定
#define abs(x) _BUILTIN_abs(x)
编译器的代码产生器会接收它。据此,如果使用者想保证像abs 这样的库函数是真正的函数可以这样写:
#undef abs
不管实现头文件提供abs的宏实现还是内建的实现。被宏定义提前和隐藏的函数原型因此也暴露出来了。
【注 158】 因此,一个信号处理者通常不能调用标准库函数。
5 例
函数 atoi 可以有以下几种用法:
——用其相应的头文件(很可能生成一个宏表达式)
#include <stdlib.h>
const char *str;
/* ... */
i = atoi(str);
——用其相应的头文件(一定是生成一个真正的函数引用)
#include <stdlib.h>
#undef atoi
const char *str;
/* ... */
i = atoi(str);
或者
#include <stdlib.h>
const char *str;
/* ... */
i = (atoi)(str);
——明确声明
extern int atoi(const char *);
const char *str;
/* ... */
i = atoi(str);
【此部分完】