问题标签 [translation-unit]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c - 在单个翻译单元中包含多个 .c 文件
在 C 中,是否经常以复制/粘贴方式.c
包含其他内部文件和静态变量/函数的文件?.c
就像一个.c
由许多文件组成的文件一样.c
,您希望所有内容都保持私有而不在头文件中声明。
例子:
交流
公元前
ch
抄送
主程序
编译
gcc - 为什么在同一个 .a 文件但不同 .o 文件中定义的弱符号不用作后备?
我有下面的树:
- main.c 调用 func1()。
- func1() 调用 func2()。
- weak.h 将 func2() 声明为弱。
- override.c 提供了 func2() 的覆盖版本。
函数1.c
函数2.c
主程序
覆盖.c
弱.h
生成文件
所有这些都运行良好,如下所示:
func2()
但是,如果我删除from的覆盖版本,override.c
如下所示:
构建通过,但最终的二进制文件在运行时给出以下错误:
而在 的符号表中./main
,func2() 是一个未解析的弱符号。
为什么没有回到func2()
原来的样子func2.c
?
毕竟all_weak.a
已经包含了一个实现func2.o
:
加 1
看来翻译单元的安排也影响了功能的回退weak
。
如果我将实现放入如下func2()
相同的文件/翻译单元func1()
中,则可以退回到原始文件func2()
。
函数1.c
的符号all_weak.a
是:
func2()
如果没有提供覆盖,代码可以正确回退到原始代码。
该链接还提到要使用 GCCalias
属性,还必须考虑翻译单元的排列。
alias (“target”) alias 属性导致声明作为另一个符号的别名发出,必须指定。例如,
void __f () { /* 做某事。*/; } void f()属性((weak, alias ("__f"))); 将 f 定义为 __f 的弱别名。在 C++ 中,必须使用目标的错位名称。如果 __f 未在同一个翻译单元中定义,则会出错。
根据维基百科:
nm 命令识别目标文件、库和可执行文件中的弱符号。在 Linux 上,如果弱默认定义可用,则弱函数符号标记为“ W ”,如果不可用,则标记为“ w ”。
添加 2 - 2021 年 8 月 7 日下午 7:54
(非常感谢@n。1.8e9-where's-my-share m。)
我试过这些:
在 func2.c
__attribute__((weak))
的func2()
定义中添加。-include weak.h
从 Makefile 中删除。
现在这些文件看起来像这样:
函数2.c
生成文件:
输出是这样的:
所以,结论是:
如果弱函数声明(就像我在 中所做的那样
weak.h
),它本质上是告诉链接器不要解析它。如果弱函数定义(就像我在 中所做的那样
func2.c
),它本质上是告诉链接器在没有找到强版本时将其用作后备。如果弱函数声明,您最好在
.o
文件中向链接器提供覆盖版本(就像我在 中所做的那样override.o
)。在这种情况下,链接器似乎仍然愿意解析.o
文件。当您无法修改源但仍想覆盖某些功能时,就会出现这种情况。
以及这里的一些引文:
如果在搜索所有输入对象后无法解析引用,则链接器将仅搜索库以解析引用. 如果需要,将根据它们在链接器命令行上的位置从左到右搜索这些库。库中的对象将按它们的归档顺序进行搜索。一旦 armlink 找到与引用匹配的符号,搜索就完成了,即使它与弱定义匹配。ELF ABI 第 4.6.1.2 节说:“弱定义不会改变从库中选择目标文件的规则。但是,如果链接集同时包含弱定义和非弱定义,则非弱定义将永远被使用。” “链接集”是链接器已加载的对象集。它不包括不需要的库中的对象。因此,不建议将其中一个包含给定符号的弱定义而另一个包含该符号的非弱定义的两个对象归档到一个库或单独的库中。
2021 年 8 月 8 日上午 8 点 47 分添加
正如@n.1.8e9-where's-my-sharem 评论的:
评论1:
不是定义的符号上的“弱”表示“在链接时不解析此符号”。链接器高兴地服从。
评论 2:
“在一个不是定义的符号上”是错误的,应该读作“在一个未定义的符号上”。
我认为“在一个未定义的符号上”,他的意思是“当前翻译单元中的一个未定义的符号”。就我而言,当我:
func2()
在单独的func2.c文件中定义- 并编译func1.c
weak.h
这些本质上告诉链接器不要解析func2()
翻译单元func1.c中的消耗。但似乎这个“不”只适用于.a
文件。如果我链接.o
文件之外的另一个文件.a
,链接器仍然愿意解析func2()
. 或者,如果func1.cfunc2()
中也定义了,链接器也会解析它。很微妙!
(到目前为止,所有这些结论都是基于我的实验结果。总结这些很微妙。如果有人能找到一些权威来源,请随时评论或回复。谢谢!)
(感谢n. 1.8e9-where's-my-share m.的评论。)
和一个相关的线程:
事后诸葛亮 - 2021 年 8 月 8 日晚上 9:55
这些微妙的行为背后没有火箭科学。这仅取决于链接器的实现方式。有时文件是模糊的。你必须尝试并处理它。(如果所有这些背后有什么大想法,请纠正我,我将不胜感激。)
compilation - 包含源文件时的翻译单元?
据我所知,翻译单元由单个实现文件 .cpp/.c 及其所有包含的标头代码组成。当在另一个 .cpp 文件中包含一个 .cpp 文件,或者在另一个 .cpp 文件中包含一个 .h 文件中包含一个 .cpp 文件时,我们这里有什么翻译单元?
c++ - 一个模块中有多少个翻译单元?
具有多个源文件 (.cpp) 的模块是否具有一个或多个翻译单元?我的理解是每个单独的源文件(.cpp)都将是它自己的翻译单元,除非它被包含在内,并且#pragma onced(我猜这是一种弊端),但我不知道这是如何在模块化程序中完成的. 如果有什么不同,那我对 Visual Studio C++ 开发特别感兴趣(C++2020 后)
c++ - 多次#included 受保护的头文件将如何在不同的翻译单元中?
我知道#inclusion 通常被描述为文本复制粘贴预处理器指令。现在,如果一个标头是#include 保护的,或者是#pragma 一次的,那么我们如何描述经过第一个翻译单元到#include 所述标头的实际发生的事情?
c++ - 在多个模块中共享的 constexpr 函数
当我使用 constexpr 函数时,我注意到一个奇怪的行为。我将代码简化为一个简化的示例。从两个不同的翻译单元(模块 A 和 B)调用两个函数。
这些模块看起来很相似。这是 mod_a.cpp:
只有一些内部常数不同。这是 mod_b.cpp:
两个模块都使用constexpr
“common.h”中定义的通用函数:
我很惊讶这两个函数都返回 12。由于#include
指令(应该只包含一些源代码),我认为两个模块之间没有交互。当我get_product
也定义为时static
,行为如预期:
mod_a()
返回 12,
mod_b()
返回 42。
我还在https://www.youtube.com/watch?v=4pKtPWcl1Go上查看了 Jason Turner 的 C++ Weekly: Stop Using 'constexpr' (And Use This!) 第 312 集。
一般使用的建议static constexpr
是一个很好的提示。
但我仍然想知道我在没有static
关键字的情况下注意到的行为是否定义明确。还是UB?或者它是一个编译器错误?
除了constexpr
函数,我还尝试了一个 C 风格的宏#define get_product() (X*Y)
,它也向我展示了预期的结果(12 和 42)。
小心
迈克尔