对函数使用宏覆盖

Use of macro overrides for functions

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

编辑: 示例:

/* 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

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

完全没有理由使用宏而不是函数。在过去,效率可能会有所提高,但现代编译器完全有能力 内联 适当的函数调用。

您必须举例说明宏覆盖的含义。同名的函数声明和宏声明不能合理地共存。比如宏一旦定义就很难再调用函数了,需要先取消宏的定义

实际接口是三个数组,例如_Ctype 数组returns 一个字符的类型。宏,例如is...宏是为了方便使用_Ctype数组。

我会说数组是固定的,宏可以适应您的用例。您可以编写新的宏,更改现有的宏。或者完全忽略宏。

这里的方法是为了速度和兼容性。使用现代 C++ 构造,人们会以不同的方式编写这样的接口。

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

C 标准允许函数被 function-like 宏覆盖。

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


§7.1.4 Use of library functions

¶1 Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow:

  • If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.
  • If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.
  • Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro.
  • Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro.185)
  • The use of #undef to remove any macro definition will also ensure that an actual function is referred to.
  • Any invocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments.186)
  • Likewise, those function-like macros described in the following subclauses may be invoked in an expression anywhere a function with a compatible return type could be called.187)
  • All object-like macros listed as expanding to integer constant expressions shall additionally be suitable for use in #if preprocessing directives.

¶2 Provided that a library function can be declared without reference to any type defined in a header, it is also permissible to declare the function and use it without including its associated header.


185) This means that an implementation shall provide an actual function for each library function, even if it also provides a macro for that function.

186) Such macros might not contain the sequence points that the corresponding function calls do.

187) Because external identifiers and some macro names beginning with an underscore are reserved, implementations may provide special semantics for such names. For example, the identifier _BUILTIN_abs could be used to indicate generation of in-line code for the abs function. Thus, the appropriate header could specify

     #define abs(x) _BUILTIN_abs(x)

for a compiler whose code generator will accept it. In this manner, a user desiring to guarantee that a given library function such as abs will be a genuine function may write

     #undef abs

whether the implementation's header provides a macro implementation of abs or a built-in implementation. The prototype for the function, which precedes and is hidden by any macro definition, is thereby revealed also.


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

以这种方式完成实施后,如果您需要将指向其中一个分类或转换函数的指针传递给其他代码,您可以这样做。如果只提供了宏,您将不得不使用一些特技来让实际函数作为指针传递。

标准仔细规定了一些宏确实必须是宏 — offsetof()va_start()va_arg() 是 spring 想到的三个。但是标准中的绝大多数功能必须作为函数实现 — 但如果实现者认为合适,可以用宏覆盖。

宏必须是 function-like 宏的要求也很重要。它允许使用名称而不用括号来获取指向函数的指针。如果宏不是 function-like(如果 header 包含类似 #define isupper _IsUpper 而不是 #define isupper(c) _IsUpper(c) 的东西)那么就不可能依赖于访问标准函数名称——而¶2 规则允许您在代码中编写(不包括 <ctype.h>):

extern int isupper(int c);

并且你可以保证库中有一个函数 isupper() 符合预期(即使还有一个 _IsUpper() 函数)。