包括一个 C 源文件在另一个?

在另一个 .c文件中使用 #include作为 .c文件是否可以(或者甚至是推荐/良好做法) ?

203262 次浏览

没有。

根据您的构建环境(您没有指定) ,您可能会发现它以您想要的方式工作。

但是,有许多环境(包括 IDE 和大量手工制作的 Makefile)需要编译 * 。C-如果发生这种情况,你可能会因为重复的符号而导致链接器错误。

一般来说,这种做法应该避免。

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

可以吗? 可以,可以编译

有什么建议吗?不。文件编译为。Obj 文件,它们在(通过链接器)编译到可执行文件(或库)之后链接在一起,因此不需要包含。在另一个文件中。你可能想做的是。H 文件,该文件列出了另一个。文件,并包含。H 档案

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

我不知道你用的是什么程序。Prj 档案。如果您正在使用诸如“ make”或 Visual Studio 之类的东西,那么只需确保您设置了要编译的文件列表,而没有设置无法独立编译的文件列表。

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

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

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

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

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

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

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

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

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

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

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

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

  • 更快的编译和链接。

但是在编辑这个文件时,您也会遇到一些麻烦,并且会失去隐含的模块性。这可以通过将源文件分割成几个文件并包含这些文件来生成一个单独的编译单元来克服。

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

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

  • 有一个主要的。C 文件,包括所有子文件。C 档案。这还可以包括公共接口的代码。

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

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

  • 保持概念上的区别。C 和。H 档案。这利用了现有的约定。不同之处在于,您的标头中将包含大量静态声明。

  • 如果您的工具链没有强加任何不这样做的理由,请将私有实现文件命名为。C 和。哦。如果使用 include 守卫,则不会产生任何代码,也不会引入任何新名称(在链接期间,您可能会得到一些空段)。其巨大的优势在于其他工具(例如 IDE)可以适当地处理这些文件。

我觉得我应该分享一下我的团队决定。C 档案。我们的体系结构主要由通过消息系统解耦的模块组成。这些消息处理程序是公共的,并且调用许多本地静态 worker 函数来完成它们的工作。当试图覆盖我们的单元测试用例时,问题出现了,因为运行这个私有实现代码的唯一方法是通过公共消息接口间接地进行。由于堆栈中的一些工作函数已经到了膝盖深的地步,要实现适当的覆盖就变成了一场噩梦。

包括.c 文件在内,我们可以找到我们感兴趣的测试机器的齿轮。

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

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

您应该像这样添加标题

#include <another.c>

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

我在 ATMEGA 微控制器的 AVR 协同工作中使用它,它能够正常工作,但是在普通的 C 语言文件中不能工作

您可以使用 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 中声明它。

许多其他的答案涵盖了你如何做到这一点,但为什么你可能不应该在正常情况下。也就是说,我要补充一下我过去这样做的原因。

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

因此,您可以创建一个包含供应商源代码的本地源文件,然后编译这个包装器 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