在另一个C中包含一个C源文件?


Answers:


113

如果使用得当,这可能是有用的技术。

假设您有一个复杂的,性能关键的子系统,该子系统具有相当小的公共接口和许多不可重用的实现代码。该代码运行到几千行,一百个左右的私有函数以及相当多的私有数据。如果使用非平凡的嵌入式系统,则可能会足够频繁地处理这种情况。

您的解决方案可能是分层,模块化和分离的,并且可以通过在不同文件中编码子系统的不同部分来有效地表示和增强这些方面。

使用C,这样做会损失很多。几乎所有工具链都为单个编译单元提供了不错的优化,但是对于任何声明为extern的东西都非常悲观。

如果将所有内容都放入一个C源模块中,则会得到-

  • 性能和代码大小的改进-在许多情况下都将内联函数调用。即使没有内联,编译器也有机会产生更有效的代码。

  • 链接级数据和功能隐藏。

  • 避免名称空间污染及其必然结果-您可以使用较少笨拙的名称。

  • 更快的编译和链接。

但是,在编辑此文件时,您也会感到一团糟,并且失去了隐含的模块化。可以通过将源分成几个文件并包括这些文件以生成一个编译单元来克服。

但是,您需要强加一些约定来适当地管理它。这些在某种程度上取决于您的工具链,但是一些通用的指针是-

  • 将公共接口放在单独的头文件中-无论如何,您都应该这样做。

  • 有一个主.c文件,其中包括所有辅助.c文件。这也可能包括公共接口的代码。

  • 使用编译器防护措施来确保外部编译单元不包含专用头文件和源模块。

  • 所有私有数据和功能都应声明为静态。

  • 保持.c和.h文件的概念区别。这利用了现有的约定。不同之处在于,标题中会包含很多静态声明。

  • 如果您的工具链没有强加任何理由,请将私有实现文件命名为.c和.h。如果使用include防护,则这些防护将不会产生代码,也不会引入任何新名称(在链接过程中,您可能会得到一些空段)。巨大的优势是其他工具(例如IDE)将适当地处理这些文件。


1
+1仍然是现实,而更好的编译器会使此方法随着时间而过时。具有链接时间优化功能的GCC 4.5是迈出的重要一步。
u0b34a0f6ae 2010年

我已经看到很多程序可以做到这一点,而当我尝试重用它们的代码时,这让我感到不安。感谢您解释为什么这样做,并建议使用防护罩(通常不会这样做)。
乔伊·亚当斯

在C51的嵌入式开发中,使用extern和多个C文件只引起了头痛。我改用一个C文件包含所有其他文件来找回我的时间。
mahesh

有关记录:GCC支持跨不同翻译单元的优化,请参阅此SO线程和GCC手册中有关链接时间优化的部分
Twonky

60

可以吗 是的,它将编译

推荐吗?no-.c文件编译为.obj文件,这些文件在编译后(由链接器链接在一起)到可执行文件(或库)中,因此无需在一个文件中包含一个.c文件。相反,您可能要做的是制作一个.h文件,该文件列出另一个.c文件中可用的函数/变量,并包括.h文件


14
同样值得注意的是,即使#included .c文件也被编译并且两个目标文件链接在一起,即使它可以编译,它也可能不会链接-您可能会得到多个定义的符号。
尼克·迈耶

1
一个小问题。我有一个头文件声明了struct +方法,还有相应的.c文件来定义它们。如果该方法将struct作为参数,如何避免将.c文件包含在定义了main方法的另一个.c文件中?
stdout

12

没有。

根据您的构建环境(未指定),您可能会发现它完全按照您想要的方式工作。

但是,有许多环境(包括IDE和许多手工制作的Makefile)都希望编译* .c-如果发生这种情况,由于符号重复,您可能最终会遇到链接器错误。

通常应避免这种做法。

如果您绝对必须#include源文件(通常应避免使用该文件),请对该文件使用其他文件后缀。


9

我以为我会遇到团队决定包含.c文件的情况。我们的架构主要由通过消息系统解耦的模块组成。这些消息处理程序是公共的,并调用许多本地静态工作者函数来完成其工作。当试图覆盖我们的单元测试用例时,就会出现问题,因为行使此私有实现代码的唯一方法是间接通过公共消息接口。堆栈中有些工人的功能并不能完全解决,这是实现适当覆盖范围的噩梦。

包括.c文件为我们提供了一种在测试中很有趣的机器上到达齿轮的方法。


6

您可以在Linux中使用gcc编译器在一个输出中链接两个c文件。假设您有两个c文件,一个是“ main.c”,另一个是“ support.c”。所以链接这两个的命令是

gcc main.c support.c -o main.out

通过这两个文件将链接到单个输出main.out要运行输出,命令将是

./main.out

如果要在support.c文件中声明的main.c中使用函数,则还应该使用extern存储类在main中声明它。


5

该文件的扩展名对大多数C编译器都无关紧要,因此它将起作用。

但是,根据您的makefile或项目设置,所包含的c文件可能会生成一个单独的目标文件。链接时可能导致重复定义符号。


3

您可以将.C或.CPP文件正确包含到其他源文件中。根据您的IDE,通常可以通过查看要包含的源文件属性来防止双链接,通常是通过右键单击它并单击属性,然后取消选中/选中编译/链接/从构建中排除它或其他任何选项。也许。否则您无法将文件包含在项目本身中,因此IDE甚至不会知道它的存在,也不会尝试对其进行编译。使用makefile,您根本不会将文件放入其中进行编译和链接。

编辑:对不起,我将其设为答案,而不是其他答案:(


1

C语言没有禁止使用此类#include,但是生成的翻译单元仍然必须是有效的C。

我不知道您使用的是哪个程序与.prj文件。如果您使用的是“ make”或Visual Studio之类的工具,则只需确保将其文件列表设置为要编译的文件即可,而不能单独编译。


0

将C文件包含到另一个文件中是合法的,但不建议这样做,除非您确切知道为什么要这样做以及要达到什么目的。
我几乎可以肯定,如果您在此处发布问题的原因,社区会找到另一个更合适的方法来实现您的目标(请注意“几乎”,因为鉴于上下文,这可能是解决方案) )。

顺便说一句,我错过了问题的第二部分。如果将C文件包含到另一个文件中,并且同时包含在项目中,则可能会出现重复符号的问题,为什么链接对象,即同一函数将被定义两次(除非它们都是静态的)。


-6

你应该像这样添加标题

#include <another.c>

注意:两个文件应放在同一位置

我在用于ATMEGA微控制器的codevison AVR中使用了它,并且可以正常工作,但在正常的C语言文件中却无法工作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.