将C函数在C标准库中定义为宏

Zen*_*lea 0 c posix c-standard-library

我一直在C头上阅读The Open Group Base Specifications和Posix Programmers手册页,这个短语重新出现了"以下内容将被声明为函数,也可能被定义为宏",我不确定这意味着什么.从声明的函数创建宏有什么意义.那不就是创建命名空间冲突吗?你也为什么要这样做呢?

Jon*_*ler 5

宏永远不会与其他名称空间冲突; 首先处理宏并自动"赢".

标准的C标准库部分的引言部分,第7.1引言 - 特别是§7.1.2,§7.1.37.1.4--涵盖了许多要点.

有些功能可以通过宏更有效地实现,而不会产生完整函数调用的开销.该标准的规则允许实现为这些函数定义宏,但需要注意许多警告.一个是宏的参数可能不会被使用多次; 另一个是标准中定义的每个函数都必须有一个函数,即使它也是作为宏实现的.

这些规则允许实现的灵活性,而不会使实现者或实现的用户负担过重.

§7.1.2标准标题

4......如果使用,标题应包含在任何外部声明或定义之外,并且应首先包含在它声明的任何函数或对象的第一个引用之前,或者它所定义的任何类型或宏之前.然而,如果在多于一个报头中声明或定义了标识符,则可以在对标识符的初始引用之后包括第二和后续关联报头.程序不应具有任何宏,其名称与包含标题之前当前定义的关键字在词典上相同,或者在标题中定义的任何宏被扩展时.

5本节中描述的类对象宏的任何定义都应扩展到必要时用括号完全保护的代码,以便它在任意表达式中分组,就像它是单个标识符一样.

6库函数的任何声明都应具有外部链接.

7.1.3保留标识符

1每个标头声明或定义其关联子条款中列出的所有标识符,并可选地声明或定义其关联的未来库方向子条款和标识符中列出的标识符,这些标识符始终保留用于任何用途或用作文件范围标识符.

  • 所有以下划线开头的标识符以及大写字母或另一个下划线始终保留用于任何用途.

  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符.

  • 如果包含任何相关标头,则保留以下任何子条款中的每个宏名称(包括未来的库方向)以供指定使用; 除非另有明确说明(见7.1.4).

  • 在以下任何子条款(包括未来的库方向)和errno中具有外部链接的所有标识符始终保留用作具有外部链接的标识符.184)

  • 在以下任何子条款中列出的具有文件范围的每个标识符(包括未来的库方向)保留用作宏名称,并且如果包括任何相关联的标题,则用作具有相同名称空间的文件范围的标识符.

2不保留其他标识符.如果程序在保留它的上下文中声明或定义标识符(除了7.1.4允许的标识符),或者将保留标识符定义为宏名称,则行为是未定义的.

3如果程序删除(使用#undef)上面列出的第一个组中的标识符的任何宏定义,则行为是未定义的.

184)与外部连接保留的标识符的列表包括math_errhandling,setjmp, va_copy,和va_end.

7.1.4库函数的使用

1以下每个陈述均适用,除非在下面的详细说明中另有明确说明:如果函数的参数具有无效值(例如函数域外的值,或者指向地址空间外的指针)程序,或空指针,或指向不可修改的存储的指针,当相应的参数不是const限定的)或类型(促销后)不具有可变数量的参数的函数,行为是未定义的.如果一个函数参数被描述为一个数组,那么实际传递给该函数的指针应该具有一个值,使得所有地址计算和对象的访问(如果指针确实指向这样一个数组的第一个元素,这将是有效的)事实上是有效的.标头中声明的任何函数可以另外实现为标头中定义的类函数宏,因此如果在包含标头时显式声明了库函数,则可以使用下面显示的技术之一来确保声明不是受到这样一个宏观的影响.通过将函数的名称括在括号中,可以在本地抑制函数的任何宏定义,因为该名称后面没有左括号,后面表示宏函数名称的扩展.出于相同的语法原因,即使它也被定义为宏,也允许获取库函数的地址.185)使用#undef删除任何宏定义也将确保引用实际函数.任何实现为宏的库函数的调用都应扩展为仅对其每个参数进行一次计算的代码,必要时用括号完全保护,因此使用任意表达式作为参数通常是安全的.186)同样,在可以调用具有兼容返回类型的函数的任何地方,可以在表达式中调用以下子条款中描述的那些类似函数的宏.187)所有列为扩展为整数常量表达式的类似对象的宏应另外适用于#if预处理指令.

2如果可以在不引用标题中定义的任何类型的情况下声明库函数,则允许声明该函数并使用它而不包括其关联的标题.

185)这意味着实现应为每个库函数提供实际函数,即使它还为该函数提供宏.

186)这些宏可能不包含相应函数调用的序列点.

187)因为保留了以下划线开头的外部标识符和一些宏名称,所以实现可以为这些名称提供特殊的语义.例如,标识符 _BUILTIN_abs可用于指示abs函数的内联代码的生成.因此,适当的标头可以指定:

#define abs(x) _BUILTIN_abs(x)
Run Code Online (Sandbox Code Playgroud)

对于代码生成器将接受它的编译器.以这种方式,希望保证诸如abs将是真正功能的给定库功能的用户可以写入

#undef abs
Run Code Online (Sandbox Code Playgroud)

实现的标头是提供宏实现abs还是内置实现.由此揭示了该函数的原型,其在任何宏定义之前并且被任何宏定义隐藏.

重点加在7.1.4

这些规则概述了实现者(编译器,更具体地说,C库)与使用该实现的程序员之间的条约要点.