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

是否将一个C源文件包含在另一个C源文件中?,c,include,c-preprocessor,code-organization,project-organization,C,Include,C Preprocessor,Code Organization,Project Organization,在另一个.c文件中包含一个.c文件是否可以(甚至建议/良好做法)?否 根据您的构建环境(您没有指定),您可能会发现它的工作方式完全符合您的需要 但是,有许多环境(IDE和许多手工制作的Makefiles)希望编译*.c-如果发生这种情况,您可能会由于重复符号而导致链接器错误 通常应避免这种做法 如果您必须包含源代码(通常应该避免),请为文件使用不同的文件后缀。可以吗?是的,它会编译的 推荐吗?no-.c文件编译为.obj文件,这些文件在编译后(由链接器)链接到可执行文件(或库)中,因此不需要将一

在另一个
.c
文件中包含
一个
.c
文件是否可以(甚至建议/良好做法)?

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

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

通常应避免这种做法


如果您必须包含源代码(通常应该避免),请为文件使用不同的文件后缀。

可以吗?是的,它会编译的


推荐吗?no-.c文件编译为.obj文件,这些文件在编译后(由链接器)链接到可执行文件(或库)中,因此不需要将一个.c文件包含在另一个.obj文件中。您可能想做的是创建一个.h文件,列出另一个.c文件中可用的函数/变量,并包含.h文件。c语言不禁止这种包含,但生成的翻译单元仍然必须是有效的c


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

文件的扩展名对大多数C编译器来说并不重要,因此它可以工作


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

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


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

正确使用,这可能是一种有用的技术

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

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

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

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

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

  • 链接级数据和函数隐藏

  • 避免名称空间污染及其必然结果—您可以使用不太笨重的名称

  • 更快的编译和链接

但是当你编辑这个文件的时候,你也会得到一个不神圣的混乱,你会失去隐含的模块性。这可以通过将源代码拆分为多个文件并包含这些文件以生成单个编译单元来克服

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

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

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

  • 使用编译器保护来确保外部编译单元不包括私有头和源模块

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

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

  • 如果您的工具链没有强加任何不这样做的理由,那么将私有实现文件命名为.c和.h。如果您使用include-guard,这些将不会生成代码,也不会引入新名称(在链接过程中可能会出现一些空段)。其巨大的优势在于其他工具(如IDE)将适当地处理这些文件


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


包含.c文件为我们提供了一种方法,可以在我们感兴趣的测试机器中找到cog。

您可以将.c或.CPP文件正确地包含到其他源文件中。 根据您的IDE,您通常可以通过查看要包含的源文件属性来防止双重链接,通常是通过右键单击它并单击
#include <another.c>
gcc main.c support.c -o main.out
./main.out
/**
 * @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
gcc main.c vendor_wrap.c -o $(CFLAGS) main.out