C 中的最小保证常量折叠

Minimum Guaranteed Constant Folding in C

问题

我很好奇是否可以保证在 C 中完成常量折叠。

我看过的地方

This link 在一个我不知道信誉的网站上随便发表评论:

All C compilers can fold integer constant expressions that are present after macro expansion (ANSI C requirement).

但是我在例如The C Programming Language, second edition(我认为已经彻底更新以说明 ANSI C 的所有细节)。但是在检查了相关词的索引后,我发现没有任何东西可以保证这一点。第 38 页和第 209 页特别接近,因为他们说任何 可以 在编译时计算的表达式都可以在可以使用常量的地方使用(如果我们是,可能有一些限制足够迂腐),它说这样的表达式“可能”在编译时被评估,而不是"will"/(一些同义词)。

我搜索了 this C89 final draft。单词 "folding" 和 "folded" 没有产生有价值的结果,搜索 "constant expression" 产生了 63 个匹配项,我检查了大约一半。兴趣的主要部分似乎与本书基本相同(它使用单词“can”而不是"may",但在这种情况下它们是同义词)。

这两个似乎在逻辑上强烈暗示每个 ANSI C 编译器都必须具有基本的常量折叠功能,但与此同时,似乎没有任何硬性禁止将常量表达式编译成在运行时计算表达式的代码(这样的实现仍然受益于常量表达式,因为编译器可以生成计算一次的代码,然后假设值不会改变,并且这样的实现可能会受到给定底层架构的限制- 例如,一些 RISC 架构必须使用两条指令来初始化某些可能的值,或者从内存位置加载它们)。

我也简单地搜索了 this C99 final draft,但是 "folding" 得到了一个没有价值的结果,而 "folded" 和 "constant" 各有一百多个匹配项,我目前找不到'分配时间进行爬取。

动机

我编写这些宏是为了在一些位运算代码中更加semantic/intent清晰:

#define UCHAR_LOW_N_BITS_m(n) (unsigned char )(UCHAR_MAX >> (CHAR_BIT - (n)))
#define UCHAR_NTH_BIT_m(n) (unsigned char )(1 << (n))

..其中 n 始终是整数文字。我想得到安慰,想听到一个令人安心的声音说 "it's okay, every C compiler in use that remotely matters will fold these constants for you"。 (P.S。我问了一个单独的问题,关于 UCHAR_NTH_BIT_m 是否应该表现得好像 bits start from the 0th or 1st bit,希望在正确的地方。)

是的,底部的宏可以变成单独的宏,例如#define UCHAR_1ST_BIT_m (unsigned char )1#define UCHAR_3RD_BIT_m (unsigned char )4 或者我碰巧在代码中需要多少 - 虽然我不确定哪一个更好,但这可能是一个有争议的问题,因为如果我想成为一个好人迂腐的语言律师类型的 C 程序员,我无法完全避免最上面的那个(必须确保代码在那些 DSP/embedded 和古老的大型机 C 实现上做正确的事情)。

一般来说,C 标准不指定某些东西是如何编译的。您永远无法保证一种实现方式比另一种更快。无法保证一定会发生常量折叠,但由于编译器必须能够在编译时计算常量表达式(以处理 #if 指令和数组声明符),因此编译器很可能会进行常量折叠。毕竟,能做常量折叠却不做是没有意义的。

在 C 标准中,编译时的术语是翻译时,编译时发生的事件可以描述为翻译期间。您可以在标准中搜索这些术语。

本标准中与您的主题最接近的是第 6.6 (C11) 节中 常量表达式 的定义。概述是:

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

请注意 "can be" 并不意味着 "must be"。我们期望一个高质量的编译器在编译时评估常量表达式,尽管这不是绝对要求。

第 6.6 节太大,无法将全部内容粘贴到此处,但通过查阅 C 标准(或 N1570 等草案),您可以了解哪些表达式被定义为 常量表达式 并且假设您的编译器在编译时评估它们是合理的。

如果n >= 0 && n < CHAR_BIT * sizeof(int) - 1,并且n是一个整数常量表达式,那么(unsigned char )(1 << (n))是一个整数常量表达式,因为:它的操作数是整数常量,它不使用常量表达式中禁止的运算符之一(例如 ++),并且允许转换,因为它从整数类型转换为另一种整数类型。

如果 n 超出此范围,则表达式不是常量表达式,并导致未定义的行为。

你的其他表达方式类似;只要 CHAR_BIT - n 落在相同的有效范围内,它就是 ICE。