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

102 c code-organization project-organization include c-preprocessor

#include.c文件中,.c文件是否可以(或者甚至是推荐/良好实践)?将它们包含在项目文件中会发生什么?

小智 108

如果使用得当,这可能是一种有用的技术.

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

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

使用C,你可以通过这样做而失去很多.几乎所有工具链都为单个编译单元提供了不错的优化,但对于任何声明为extern的东西都非常悲观.

如果你把所有东西放到一个C源模块中,你得到 -

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

  • 链接级别数据和功能隐藏.

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

  • 更快的编译和链接.

但是在编辑这个文件时你也会得到一个不圣洁的混乱而你失去了隐含的模块性.这可以通过将源拆分为多个文件来解决,并将这些文件包括在内以生成单个编译单元.

您需要强制执行一些约定来正确管理它.这些在某种程度上取决于你的工具链,但一些一般指示是 -

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

  • 有一个主.c文件,包含所有子.c文件.这还可以包括公共接口的代码.

  • 使用编译器保护来确保外部编译单元不包含专用头和源模块.

  • 应将所有私有数据和函数声明为静态.

  • 保持.c和.h文件之间的概念区别.这利用了现有的惯例.不同之处在于您的标头中会有很多静态声明.

  • 如果您的工具链没有强加任何理由,请将私有实现文件命名为.c和.h.如果使用包含保护,这些将不会生成代码并且不会引入新名称(在链接期间最终可能会出现一些空段).巨大的优势是其他工具(例如IDE)将适当地处理这些文件.

  • 我见过很多程序都这样做,当我尝试重用它们的代码时,这让我很紧张。感谢您解释他们为什么这样做,并建议使用警卫(通常没有这样做)。 (3认同)
  • +1 这仍然是现实,而更好的编译器会使这种方法随着时间的推移而过时。带有链接时优化的 GCC 4.5 是前进的一大步。 (2认同)
  • 在C51的嵌入式开发中,使用extern和多个C文件只会让人头疼。我正在切换到让一个 C 文件包含所有其他文件,以节省我的时间。 (2认同)

Ste*_*owe 49

好吗?是的,它会编译

推荐吗?no - .c文件编译为.obj文件,它们在编译后(通过链接器)链接到可执行文件(或库)中,因此不需要在另一个文件中包含一个.c文件.您可能想要做的是创建一个.h文件,列出另一个.c文件中可用的函数/变量,并包含.h文件

  • 另外值得注意的是,即使它编译,如果#included .c文件也被编译并且两个目标文件链接在一起,它可能不会链接 - 你可能最终得到多个定义的符号. (13认同)

And*_*mbe 11

没有.

根据您的构建环境(您未指定),您可能会发现它的工作方式完全符合您的需要.

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

通常应该避免这种做法.

如果您绝对必须#include source(通常应该避免使用),请为该文件使用不同的文件后缀.


小智 9

我想我会分享我的团队决定包含.c文件的情况.我们的架构主要包括通过消息系统解耦的模块.这些消息处理程序是公共的,并调用许多本地静态工作程序函数来完成它们的工作.在尝试覆盖我们的单元测试用例时出现问题,因为执行此私有实现代码的唯一方法是间接通过公共消息接口.由于一些工人职能在膝盖深处,这对于实现适当的覆盖是一场噩梦.

包括.c文件让我们有机会在我们测试中感兴趣的机器中找到cog.


sum*_*roy 7

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

gcc main.c support.c -o main.out
Run Code Online (Sandbox Code Playgroud)

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

./main.out
Run Code Online (Sandbox Code Playgroud)

如果您在 main.c 中使用在 support.c 文件中声明的函数,那么您应该在 main 中也使用 extern 存储类声明它。


HS.*_*HS. 5

对于大多数C编译器来说,文件的扩展名无关紧要,因此它可以正常工作.

但是,根据您的makefile或项目设置,包含的c文件可能会生成单独的对象文件.链接时可能会导致双重定义的符号.