允许将函数实现为宏的 C 标准是否有任何限制? [英] Are there any restrictions to the C standard allowing functions to be implemented as macros?

查看:18
本文介绍了允许将函数实现为宏的 C 标准是否有任何限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,除了提供函数声明外,C 标准头文件还可能提供掩码宏"以加快处理速度.比如我包含ctype.h,头文件会声明

Often, in addition to providing a function declaration, C standard headers may provide a "masking macro" to make things speedier. For example, if I include ctype.h, the header file will declare

int isdigit(int c);

但它也可以用宏来掩盖声明.我相信这是一个符合 C 标准的可移植 isdigit 宏:

But it may also mask the declaration with a macro. I believe this is a portable isdigit macro according to the C standard:

#define isdigit(c) ((c) >= '0' && (c) <= '9')

当然,这个宏也很危险,因为如果你在定义宏时这样做,它会引入未定义的行为:

Of course, this macro is also dangerous because it introduces undefined behavior if you do this while the macro is defined:

int c = 'A';
printf("%d
", isdigit(c++));

为了避免在这种假设情况下出现 UB,我必须用括号括住函数名称:(isdigit)(c++).所以,我的问题是:标准头文件可以定义什么样的掩码宏有任何限制吗?如果参数表达式有副作用,它们是否保证不会导致未定义的行为,或者它们在技术上是否允许具有我们上面看到的奇怪行为?限制在哪里?

To avoid UB in this hypothetical case, I have to surround the function name with parens: (isdigit)(c++). So, my question is: are there any restrictions to what sort of masking macros a standard header can define? Are they guaranteed to not cause undefined behavior if an argument expression has side effects, or are they technically allowed to have weird behavior such as we see above? Where are the limits?

推荐答案

根据 C11 7.1.4.1,使用库函数",特别是下面引用的最后一部分:

Per C11 7.1.4.1, "Use of Library Functions", particularly the last part quoted below:

在头文件中声明的任何函数都可以另外实现为头文件中定义的类函数宏,所以如果一个库函数在包含其标头时显式声明,其中之一下面显示的技术可用于确保声明不受这样一个宏的影响....出于同样的句法原因,它是允许获取库函数的地址,即使它也是定义为宏.使用 #undef 删除任何宏定义还将确保引用实际功能.任何调用作为宏实现的库函数应扩展为对每个参数只计算一次的代码,必要时用括号完全保护,所以通常是可以安全地使用任意表达式作为参数.

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....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. 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.

请注意,末尾的一般"很重要,并且有明确的例外.C11 7.21.7.5.2 说getc 函数等价于 fgetc,除了如果它被实现为宏,它可以评估 stream 不止一次,所以参数永远不应该是一个有副作用的表达式",与 C11 7.21.7.7.2 中的 putc 类似的语言.在这种特殊情况下,stream 是一个 FILE *,因此在正常情况下,将其作为具有副作用的表达式会有点奇怪,但它可能会发生.对于它们的宽字符对应物 putwc()getwc() 也是如此.我不知道有任何其他类似的例外情况.

Note that the "generally" at the end is important, there, and there are explicit exceptions. C11 7.21.7.5.2 says "the getc function is equivalent to fgetc, except that if it is implemented as a macro, it may evaluate stream more than once, so the argument should never be an expression with side effects", with similar language for putc in C11 7.21.7.7.2. In this particular case, stream is a FILE *, so under normal circumstances it would be kind of weird to have this as an expression with side-effects, but it could happen. The same is also true for their wide character counterparts, putwc() and getwc(). I am not aware of any other exceptions like this.

这篇关于允许将函数实现为宏的 C 标准是否有任何限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆