C 标准中是否有要求编译(和链接)二进制文件中的函数将按照它们在 C 文件中写入的顺序出现?
请假设在下面的示例中编译器没有删除/内联任何函数,并且它们都存在于二进制文件中。问题不在于编译器可能对空函数做什么,而在于函数的顺序。
例如,如果我编译 example.c:
void bar() { }
void foo() { bar(); }
int main() { foo(); }
我可以确定它foo
会bar
在输出文件中出现吗?
C 标准中是否有要求编译(和链接)二进制文件中的函数将按照它们在 C 文件中写入的顺序出现?
请假设在下面的示例中编译器没有删除/内联任何函数,并且它们都存在于二进制文件中。问题不在于编译器可能对空函数做什么,而在于函数的顺序。
例如,如果我编译 example.c:
void bar() { }
void foo() { bar(); }
int main() { foo(); }
我可以确定它foo
会bar
在输出文件中出现吗?
不,C 标准中没有这样的要求。在编译和链接方面,仅明确提到了函数的特定属性,例如外部或静态链接等,但即使这些属性也大多以独立于实现的方式进行描述。到目前为止,任何标准文档中都没有条款(据我所知)对可执行文件中的符号顺序施加期望。
语言中对此没有规定。通常,它们确实按照您查看代码时所期望的顺序出现,但没有什么说编译器不能构建函数堆栈,并以完全相反的顺序输出它们——当然,一个没有被调用的函数可以删除,类似地,可以删除内联函数并且编译器可以确定它不需要外部引用的原始形式。
您可以通过 找到函数的位置char *ptr = (char *)bar;
。
编辑:请注意,通过获取函数的地址,您可能会更改函数的内联,因此不要指望这是确定编译器“在正常情况下”做什么的好方法。
仅通过编译器开关是无法控制的。你需要一个两步的过程;在这里为 ELF(UN*X 对象文件格式)说明了这一点,但对于 Windows PE 对象,它可以与此类似。
hot
, cold
, ...)可能已经满足您的需求,但如果没有,并且特定的排序/特定位置是绝对必要的,那么...实际的代码/数据放置发生在链接时——链接器可以控制“组成对象”的目标代码放置——源对象的 ELF 部分——在生成的目标“复合对象”内,即生成的可执行文件/库。这通过链接器脚本发生。如果使用自定义链接器脚本指示这样做,链接器将把输入部分放在用户指定的位置/以用户指定的顺序到输出对象中。请参阅有关链接器脚本的GNU binutils (ld) 手册。
结果(反映链接器实际将输入的各个部分放入输出的位置)您可以请求生成链接器映射文件;如果您使用非默认/自定义链接器脚本来严格控制代码放置,那么您应该指示链接器执行此操作,以便您可以交叉检查它是否符合您的要求。否则,如果您使用链接器的默认值,映射文件会告诉您在没有特定覆盖的情况下做了什么——这可能是也可能不是您想要的,但它至少是一种检查方法。
没有这样的要求。但是,在您的示例中,如果 bar() 出现在 foo() 之后,则 foo 将认为 bar() 是一个返回整数的尚未定义的函数。