6

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

int isdigit(int c);

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

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

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

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

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

4

2 回答 2

5

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

头文件中声明的任何函数都可以额外实现为头文件中定义的类函数宏,因此如果在包含头文件时显式声明库函数,则可以使用下面显示的技术之一来确保声明不受这种宏的影响....出于相同的语法原因,即使库函数也被定义为宏,也允许获取库函数的地址。使用#undef删除任何宏定义也将确保引用实际函数。作为宏实现的库函数的任何调用都应扩展为仅对其每个参数进行一次评估的代码,必要时由括号完全保护,因此使用任意表达式作为参数通常是安全的。

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

于 2013-10-10T14:43:03.563 回答
1

从这里:http ://www.qnx.com/developers/docs/6.5.0/index.jsp?topic=%2Fcom.qnx.doc.dinkum_en_ecpp%2Flib_over.html

无论表达式执行宏扩展还是调用函数,具有副作用的参数的计算方式都相同。函数 getc 和 putc 的宏是此规则的明确例外。

我发现其他一些参考资料也提到了同样的事情(尽管没有直接引用 C 标准)。

因此,似乎限制仅此而已getc(),并且putc()可能会以您预见的方式造成严重破坏。除了这两个,你应该是安全的,假设你的平台至少是理智的或合规的。

于 2013-10-10T14:43:29.567 回答