C

时间:2016-04-08 08:01:15

标签: c language-lawyer constant-expression constantfolding

问题

我很好奇是否有关于在C中进行常量折叠的保证。

我在哪看

This link在我不知道其声誉的网站上发表了一个随意的评论:

  

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

但是我没有看到任何例子,例如C编程语言,第二版(我认为已经彻底更新,以解释ANSI C的所有细节)。但是在检查了相关单词的引用索引后,我发现没有任何承诺。第38页和第209页特别接近,因为他们说任何可以在编译时进行评估的表达式都可以在可以使用常量的地方使用(如果我们足够迂腐,可能会有一些限制) ,并且它表示这样的表达" 可能"在编译时进行评估,而不是"将" /(某些同义词)。

我搜索了this C89 final draft。 "折叠"并且"折叠"没有产生任何价值的结果,并且搜索"常数表达"生成63场比赛,其中我检查了一半。兴趣的主要部分似乎基本上与书相同(​​它使用单词" 可以"而不是"可能",但这些是同义词在这种情况下)。

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

我还简要搜索了this C99 final draft,但是"折叠"产生了一个没有价值的结果,而#34;折叠了#34;和"常数"每次有超过一百场比赛我目前无法分配时间来爬行。

动机

我在一些比特麻烦的代码中写了这些宏以获得更多的语义/意图清晰度:

#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始终是整数文字。我想要安慰,听到一个令人安心的声音说“没关系,所使用的每个远程重要的C编译器都会为你折叠这些常量”#34;。 (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 /嵌入式和古代大型机C实现上做正确的事情)。

2 个答案:

答案 0 :(得分:5)

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

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

  

常量表达式可以在转换期间而不是运行时进行评估,因此可以在常量可能的任何位置使用。

请注意&#34;可以&#34;并不意味着&#34;必须是&#34;。我们期望一个高质量的编译器在编译时评估常量表达式,尽管这不是绝对的要求。

第6.6节太大了,无法在此处粘贴整个内容,但是通过查阅C标准(或者诸如N1570之类的草案),您可以了解哪些表达式被定义为常量表达式,它会可以合理地假设您的编译器在编译时对它们进行评估。

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

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

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

答案 1 :(得分:4)

通常,C标准没有规定如何编译。你无法保证一种实现某种方式的方法比另一种更快。无法保证必须进行常量折叠,但由于编译器必须能够在编译时计算常量表达式(以处理#if指令和数组声明符),编译器很可能会进行常量折叠。毕竟,能够不断进行折叠然后不进行折叠是没有意义的。