编译器对数组声明大小的评估。啥时候发生?

Posted

技术标签:

【中文标题】编译器对数组声明大小的评估。啥时候发生?【英文标题】:A compiler's evaluation of the size of an array's declaration. When does it take place?编译器对数组声明大小的评估。什么时候发生? 【发布时间】:2020-02-17 08:59:57 【问题描述】:

这更多是为了清晰或更好地理解编译器的内部工作原理:我开始研究编译器设计和编译器理论。

通常在堆栈上声明数组的大小时,必须在编译时知道它,这是可以理解的,但并非总是如此。

我想知道的是;何时进行此评估?它是否在预编译器、标记器、语法分析等过程中发生?此外,它是否取决于正在使用的特定编译器?最后,此评估的时间点是否指定为语言标准内编译器的任何特定阶段?

伪代码sn-p。 C 或 C++

int main() 
    int x[5]; // When does the evaluation of the 5 for the array's size take place 
              // during the compilation process? 
              // Does it take place during pre-compiler or normal compilation time. 
    return 0;

【问题讨论】:

@HolyBlackCat 这不是我想要的...我会给出一个伪代码 sn-p。 我猜是C phase 8(对C++一无所知) 我问这个是为了清楚...它必须更多地与编译器的内部工作及其设计有关...以及他们如何解释或评估一个数组,其中它的大小必须是恒定且已知的在编译时...这是在预处理阶段还是在正常编译期间发生? 预处理器与数组大小无关。它甚至不明白数组是什么。 @FrancisCugler 你的问题有 C 和 C++ 标签,这就是为什么我决定指出 C 和 C++ 之间的行为差​​异。 【参考方案1】:

C 标准规定了八个翻译阶段:

    物理源多字节字符和三字符序列映射到源字符集的字符。

    后跟换行符的每个反斜杠都会被删除(将两行拼接在一起)。

    源字符被分组为预处理标记,每个空白字符序列被一个空格替换,但保留换行符。

    执行预处理指令和 _Pragma 运算符,扩展宏调用。

    字符串和字符常量中的源字符转换为执行字符集。

    连接相邻的字符串文字。

    每个预处理标记都被转换为语法标记,并丢弃空格字符分隔的标记。生成的标记被分析和翻译(编译)。

    所有外部引用均已解析(程序已链接)。

因此,在第 7 阶段解决了恒定数组维度。但是,这些阶段主要是概念性的。这些阶段解释了如何理解 C 语言,而不是编译器必须如何执行。

对于产生对象模块的编译器,具有静态存储时长的数组大小必须在写入对象信息之前解决,因为数组大小影响数据布局,必须在对象模块中完整描述。理论上,可以在程序实际执行需要它们的代码之前处理具有自动存储持续时间的数组大小,因为这对于可变长度数组来说是必然的。但是,这样做会很浪费,因为在编译期间很容易处理常量数组大小,并且最好在编译时计算必要的值(例如输入函数时要保留的堆栈空间量),而不是比执行期间。因此我们可以预期,普通编译器在编译期间(即在完成翻译单元的目标模块之前)以及在第 6 阶段之后在概念上解析所有常量数组大小。

翻译过程中解析数组大小的点的其他标识取决于编译器实现(或通常的 C 实现)的内部细节。

【讨论】:

【参考方案2】:

在阅读了来自不同用户的 cmets 之后,我得出的结论是,标准中没有任何具体内容。 C 和 C++ 在堆栈框架内的语言中是否允许可变大小数组的实现细节上存在差异。

在编译器设计方面,预处理器没有上下文。当涉及到编译器的阶段时,它取决于它属于哪个阶段的语言,而且编译器和它们的设计之间似乎也是不可知的。看来,该评估发生在哪个阶段取决于编译器的实现设计。

一些 C++ 编译器可能会在语法分析期间执行此操作,而其他编译器可能会在标记化期间执行此操作。所以最后,确定何时真正发生的唯一真正方法是从内到外了解特定的编译器,并通读它自己的源代码以了解它是如何设计的,并逐步完成编译阶段或阶段。

感谢大家的意见和反馈。如果我错了,请务必通过在此答案下发表评论来纠正我。

【讨论】:

以上是关于编译器对数组声明大小的评估。啥时候发生?的主要内容,如果未能解决你的问题,请参考以下文章

C语言中volatile在啥情况下使用

Rails 啥时候编译 CoffeeScript?

PHP属性decleration接受数组,不接受可在编译过程中评估的任何其他表达式

编写程序的时候,定义函数,啥时候用int,啥时候用scanf?

声明一个动态大小的数组

TypeScript:如何在编译时声明固定大小的数组以进行类型检查