首页 > 解决方案 > 对函数使用宏覆盖

问题描述

我正在阅读 C 库中标头的实现,在那里我遇到了函数的宏覆盖以及函数声明。我想知道这有什么用,即应该使用宏还是函数,需要覆盖什么?

编辑:示例:

/* ctype.h standard header */
#ifndef _CTYPE
#define _CTYPE
/* Ctype code b i t s */
#define 0x200 /* extra alphabetic */
#define _XS 0x100 /* extra space */
#define _BB 0x80 /* BEL, BS, etc. */
#define _CN 0x40 /*CR, FF, HT, NL, V T */
#define _DI 0x20 /* '0'_' 9' */
#define _LO 0x10 /* 'a'_'2'*/
#define _PU 0x08 /* punctuation */
#define _SP 0x04 /* space */
#define _UP 0x02 /* 'A' _ ' Z ' */
#define _XD 0x01 /* 'Or_'9', 'A'_'Fr, ' a r _ ' f r * /
/* ********declarations********** */
int isalnum(int) , isalpha (int) , iscntrl (iny) , isdigit (int) ;
int isgraph (int) , islower (int) , isprint (int) , ispunct (int) ;
int isspace (int) , isupper (int) , isxdigit (int) ;
int tolower (int) , toupper (int) ;
extern const short *_Ctype, *_Tolower, *_Toupper;

/************ macro overrides*********** */
#define isalnum(c) (_Ctype [ (int)(C) ] & (_DI | _LO | _UP | _XA) )
#define isalpha (c) (_Ctype [ (int)(C) ] & (_LO | _UP | _XA) )
#define i s c n t r l (c) (_Ctype [ (int)(C) ] & (_BB | _CN) )
#define isdigit (c) (_Ctype [ (int)(C) ] & _DI)
#define isgraph (c) (_Ctype [ (int)(C) ] & (_DI | _LO| _PU| _UP | _XA) )
#define islower (c) (_Ctype [ (int)(C) ] & _LO)
#define isprint (c) \
(_Ctype[(int) (c)1 & (_DI| _LO| _PU| _SP| _UP| _XA))
#define ispunct (c) (_Ctype [ ( int ) (c) ] & _PU)
#define isspace (c) (_Ctype [ ( int ) (c) ] & (_CN | _SP | _XS) )
#define isupper (c) (_Ctype [ ( int ) (c) ] & _UP)
#define isxdigit (c) (_Ctype [ ( int ) (c) ] & _XD)
#define tolower (c) _Tolower [ ( int ) (c) ]
#define toupper (c) _Toupper [ ( int ) (c) ]
#endif

*更不用说函数定义在单独的文件中

标签: c++cc-libraries

解决方案


C 标准要求它指定的函数必须定义为函数,并且它们必须在适当的头文件中声明,以便您可以传递指向函数的指针。

C 标准允许函数被类似函数的宏覆盖。

该标准有一个坚实的规则块,我已将其重新格式化为项目符号列表。前两个要点与问题没有直接关系。


§7.1.4 库函数的使用

¶1 除非在随后的详细说明中另有明确说明,否则以下每个陈述均适用:

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

¶2 如果可以在不引用头文件中定义的任何类型的情况下声明库函数,则也允许声明函数并在不包括其关联头文件的情况下使用它。


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

186)此类宏可能不包含相应函数调用所做的序列点。

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

     #define abs(x) _BUILTIN_abs(x)

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

     #undef abs

实现的头文件是提供宏实现abs还是内置实现。函数的原型,它在任何宏定义之前并被任何宏定义隐藏,因此也被揭示了。


问题中的标题说明了保留标识符的使用(§7.1.3 保留标识符](http://port70.net/~nsz/c/c11/n1570.html#7.1.3))。它声明了<ctype.h>标头指定要声明的函数。它提供了覆盖这些函数的宏,相信使用这些函数会比调用实现数组访问的函数更快。

With the implementation done that way, if you need to pass a pointer to one of the classification or conversion functions to some other code, you can do so. If only the macros were provided, you'd have to pull some stunts to get actual functions to pass as pointers.

The standard carefully stipulates that a few macros really must be macros — offsetof() and va_start() and va_arg() are three that spring to mind. But the vast majority of the functions in the standard must be implemented as functions — but may be overridden by a macro if the implementers think that is appropriate.

The requirement that the macros be function-like macros is important too. It allows the name to be used without being followed by parentheses to get a pointer to the function. If the macros were not function-like (if the header contained something like #define isupper _IsUpper instead of #define isupper(c) _IsUpper(c)) then it would be impossible to rely on accessing the standard function name — whereas the ¶2 rule allows you to write in your code (without including <ctype.h>):

extern int isupper(int c);

and you will be guaranteed that there is a function isupper() in the library that matches the expectations (even if there is also an _IsUpper() function).


推荐阅读