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

时间:2008-10-24 07:03:01

标签: c include c-preprocessor code-organization project-organization

对于另一个.c文件中的#include .c文件,它是否正常(甚至是推荐/良好做法)?将它们包含在项目文件中会发生什么?

11 个答案:

答案 0 :(得分:105)

使用得当,这可能是一种有用的技术。

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

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

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

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

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

  • 链接级别数据&功能隐藏。

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

  • 加快编译速度键。

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

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

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

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

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

  • 所有私人数据&函数应声明为static。

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

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

答案 1 :(得分:49)

没关系?是的,它会编译

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

答案 2 :(得分:11)

没有

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

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

通常应该避免这种做法。

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

答案 3 :(得分:8)

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

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

答案 4 :(得分:5)

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

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

答案 5 :(得分:3)

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

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

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

./main.out

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

答案 6 :(得分:2)

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

编辑:对不起,我把它作为答案,而不是回复其他答案:(

答案 7 :(得分:1)

C语言不禁止那种#include,但结果翻译单元仍然必须是有效的C.

我不知道您正在使用.prj文件的程序。如果您正在使用“make”或Visual Studio等内容,请确保将其文件列表设置为无需无法独立编译的文件列表。

答案 8 :(得分:0)

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

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

答案 9 :(得分:-1)

许多其他答案不仅涵盖了如何执行此操作,而且还解释了为什么在正常情况下不应该这样做。就是说,我将补充为什么我过去做过。

在嵌入式开发中,通常将芯片供应商的源代码作为已编译文件的一部分。问题是那些供应商可能没有与您的组织相同的样式指南或标准警告/错误标记设置。

因此,您可以创建一个包含供应商源代码的本地源文件,然后编译该包装C文件,以消除所包含的源中的任何问题以及该源所包含的任何标头。例如:

/**
 * @file   vendor_wrap.c
 * @brief  vendor source code wrapper to prevent warnings
 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnested-externs"
#include "vendor_source_code.c"
#pragma GCC diagnostic pop

这允许您使用标准的编译器标志和设置以及代码中特定的例外情况来使用较为简单的Make脚本,而不是在脚本中为某些文件使用自定义标志。

gcc main.c vendor_wrap.c -o $(CFLAGS) main.out

答案 10 :(得分:-6)

你应该像这样添加标题

#include <another.c>

注意:两个文件都应放在同一个地方