137

另一个文件中#include的文件可以(甚至推荐/良好做法)吗?.c.c

4

11 回答 11

133

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

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

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

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

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

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

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

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

  • 更快的编译和链接。

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

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

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

  • 拥有一个包含所有子 .c 文件的主 .c 文件。这也可能包括公共接口的代码。

  • 使用编译器保护确保私有头文件和源模块不包含在外部编译单元中。

  • 所有私有数据和函数都应该声明为静态的。

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

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

于 2009-07-10T12:31:32.933 回答
74

可以吗?是的,它会编译

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

于 2008-10-24T07:12:11.863 回答
17

不。

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

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

通常应避免这种做法。

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

于 2008-10-24T07:10:10.683 回答
10

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

包含 .c 文件为我们提供了一种访问我们感兴趣的测试机器中的 cog 的方法。

于 2010-10-05T18:45:59.577 回答
8

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

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

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

./main.out

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

于 2016-08-21T11:36:33.730 回答
5

文件的扩展名对大多数 C 编译器来说并不重要,所以它会起作用。

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

于 2008-10-24T07:28:17.510 回答
3

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

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

于 2014-07-10T01:02:28.647 回答
1

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

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

于 2008-10-24T07:28:21.207 回答
0

C 语言不禁止这种#include,但生成的翻译单元仍然必须是有效的 C。

我不知道您在使用什么程序来处理 .prj 文件。如果您使用诸如“make”或 Visual Studio 之类的东西,只需确保将其文件列表设置为要编译的文件,而没有无法独立编译的文件。

于 2008-10-24T07:15:14.230 回答
-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
于 2020-09-03T14:49:49.640 回答
-9

你应该像这样添加标题

#include <another.c>

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

我在用于 ATMEGA 微控制器的 codevison AVR 中使用它并真正工作,但它不能在普通 C 语言文件中工作

于 2016-08-15T10:32:03.323 回答