3

问题

我很好奇是否有任何关于在 C 中进行恒定折叠的保证。

我看过的地方

一个我不知道声誉的网站上的这个链接提出了一个临时评论:

所有 C 编译器都可以折叠宏扩展后出现的整数常量表达式(ANSI C 要求)。

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

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

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

我还简要搜索了这个 C99 最终草案,但“折叠”产生了一个没有价值的结果,而“折叠”和“常量”各有一百多个匹配项,我目前无法分配时间来爬取。

动机

我编写了这些宏是为了在一些有点旋转的代码中更清晰地表达语义/意图:

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

..wheren始终是整数文字。我想得到安慰,听到一个令人放心的声音说“没关系,使用的每个远程重要的 C 编译器都会为你折叠这些常量”。(PS 我问了一个单独的问题,即是否UCHAR_NTH_BIT_m应该像位从第 0 位或第 1 位开始一样,希望在正确的位置。)

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

4

2 回答 2

7

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

The closest thing in this standard to your topic is the definition of constant expression in section 6.6 (C11). The overview is:

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

Note that "can be" does not mean "must be". We would expect a good quality compiler to evaluate constant expressions at compile-time although this is not an absolute requirement.

Section 6.6 is too big to paste the whole thing here, but by consulting the C Standard (or a draft such as N1570) you can read about which expressions are defined as constant expressions and it would be reasonable to assume that your compiler evaluates them at compile-time.

If n >= 0 && n < CHAR_BIT * sizeof(int) - 1, and n is an integer constant expression, then (unsigned char )(1 << (n)) is an integer constant expression because: its operands are integer constants, it doesn't use one of the operators which are forbidden in constant expressions (e.g. ++), and the cast is allowed because it casts from integer type to another integer type.

If n is outside this range then the expression is not a constant expression, and causes undefined behaviour.

Your other expression is similar; it is an ICE so long as CHAR_BIT - n falls in that same valid range.

于 2016-04-08T08:22:39.670 回答
5

通常,C 标准不指定如何编译某些内容。你永远无法保证一种实现某事的方法比另一种更快。不能保证必须发生常量折叠,但由于编译器必须能够在编译时评估常量表达式(以处理#if指令和数组声明符),编译器很可能会进行常量折叠。毕竟,能够进行持续折叠但又不这样做是没有意义的。

于 2016-04-08T08:12:09.077 回答