问题描述
我很惊讶 C 不保证在编译(翻译)时计算某些(常量)表达式。
C11(6.6 常量表达式)(强调):
一个常量表达式可以在翻译过程中而不是运行时进行计算,...
因此,有两个问题:
- 是否有一种标准方法可以保证在编译(翻译)时对某个(常量)表达式求值?
- 额外的问题:为什么C不保证呢?保证它的(技术)障碍是什么?
解决方法
C 标准通常无法分别描述在编译时和运行时可能发生的情况。整个语言基于 C17 5.1.2.3 中“抽象机器”的(相当无用的)概念
本国际标准中的语义描述描述了一个
与优化问题无关的抽象机器。
...
一个表达式的求值一般包括两个值
副作用的计算和启动...
在抽象机中,所有表达式都按照语义指定的方式进行评估。一个
实际实现不需要计算表达式的一部分,如果它可以推断出它的
不使用价值,也不会产生任何需要的副作用...
上面包含了evaluation的正式定义,这也没什么帮助。基本上,只要尊重副作用和“可观察的行为”,实现就可以自由地以优化的方式做任何事情。没有规定编译器必须执行某些优化。构建一个完全没有优化的编译器是可能的,它仍然可以是一个符合要求的实现。
至于 6.6 和常量表达式,目的肯定是说明什么是有效的编译时常量的规则。如果在编译时不计算整数常量表达式,那么生成可执行文件就变得非常不切实际,以至于整个语言都会崩溃。间接地,除非在编译时评估常量表达式,否则有许多要求无法满足。
例如,如果在编译时不知道静态存储持续时间对象的数组大小,那么您将如何生成用于初始化它们的启动代码?它必须在调用 main() 之前发生 - 这实际上是由 5.1.2 保证的:
所有具有静态存储期的对象都应在程序启动前进行初始化(设置为其初始值)。
如果 main 包含 static int arr [5] = {1,2,3,4,5};
,那么当我们到达声明时,这个对象必须已经被初始化。因此,如果我的示例中的整数常量 5
(整数常量表达式)实际上是在编译时计算的,我们只能满足 5.1.2。
这可能只是英语的轻度歧义用法:术语“can”在这里(恕我直言)用于表达表达式是编译时可评估的逻辑可能性,而不是一个可选,实现指令。
如果我们从标准中引用您引用的段落的全文(粗斜体是我的),那么我相信,可以保证评估有效 发生在编译时:
6.6 常量表达式
...
说明
2 可以在翻译期间而不是运行时评估常量表达式,因此
可以用在常量可能存在的任何地方。