【问题标题】:Organization of C filesC 文件的组织
【发布时间】:2010-09-08 01:19:55
【问题描述】:

我习惯于在一个 C 文件中完成所有编码。但是,我正在从事一个足够大的项目,以至于这样做变得不切实际。我一直将它们#include 在一起,但我遇到过我多次#include 某些文件的情况,等等。我听说过 .h 文件,但我不确定它们的功能是什么(或者为什么有 2 个文件比 1 个更好)。

我应该使用什么策略来组织我的代码?是否可以将特定文件的“公共”功能与“私人”功能分开?

This 问题引发了我的询问。 tea.h 文件不引用tea.c 文件。编译器是否“知道”每个 .h 文件都有对应的 .c 文件?

【问题讨论】:

    标签: c header file-organization


    【解决方案1】:

    您应该将 .h 文件视为 .c 文件的接口文件。每个 .c 文件都代表一个具有一定功能的模块。如果 .c 文件中的函数被其他模块(即其他 .c 文件)使用,则将函数原型放在 .h 接口文件中。通过将接口文件包含在您的原始模块 .c 文件和您需要该功能的所有其他 .c 文件中,您可以使该功能可用于其他模块。

    如果您只需要某个 .c 文件(而不是任何其他模块)中的函数,请将其范围声明为静态。这意味着它只能从定义它的 c 文件中调用。

    跨多个模块使用的变量也是如此。它们应该放在头文件中,并且必须用关键字“extern”标记。注意:对于函数,关键字“extern”是可选的。函数始终被视为“外部”。

    头文件中的包含保护有助于避免多次包含同一个头文件。

    例如:

    模块1.c:

    #include“模块1.h” 静态无效 MyLocalFunction(void); 静态无符号整数 MyLocalVariable; 无符号整数 MyExternVariable; 无效我的外部函数(无效) { MyLocalVariable = 1u; /* 做一点事 */ MyLocalFunction(); } 静态无效 MyLocalFunction(无效) { /* 做一点事 */ 我的外部变量 = 2u; }

    模块1.h:

    #ifndef __MODULE1.H #define __MODULE1.H extern unsigned int MyExternVariable; 无效我的外部函数(无效); #万一

    模块2.c

    #include "模块.1.h" 静态无效 MyLocalFunction(void); 静态无效 MyLocalFunction(无效) { 我的外部变量 = 1u; 我的外部函数(); }

    【讨论】:

      【解决方案2】:

      尽量让每个 .c 文件都专注于特定的功能领域。使用相应的 .h 文件来声明这些函数。

      每个 .h 文件都应该有一个围绕其内容的“标题”保护。例如:

      #ifndef ACCOUNTS_H
      #define ACCOUNTS_H
      ....
      #endif
      

      这样,您可以根据需要多次包含“accounts.h”,并且在特定编译单元中第一次看到它将是唯一实际提取其内容的编译单元。

      【讨论】:

        【解决方案3】:

        编译器

        您可以在this topic 看到一个 C“模块”示例 - 请注意,有两个文件 - 头文件 tea.h 和代码 tea.c。您在头文件中声明您希望其他程序访问的所有公共定义、变量和函数原型。在您的主项目中,您将 #include 并且该代码现在可以访问标题中提到的 tea 模块的函数和变量。

        在那之后它变得有点复杂。如果您使用 Visual Studio 和许多其他为您管理构建的 IDE,请忽略这部分 - 它们负责编译和链接对象。

        链接器

        当您编译两个单独的 C 文件时,编译器会生成单独的目标文件 - 因此 main.c 变为 main.o,tea.c 变为 tea.o。链接器的工作是查看所有目标文件(您的 main.o 和 tea.o),并匹配引用 - 因此,当您在 main 中调用 tea 函数时,链接器会修改该调用,因此它确实调用了正确的在茶中发挥作用。链接器生成可执行文件。

        great tutorial 对此主题进行了更深入的探讨,包括范围和您将遇到的其他问题。

        祝你好运!

        -亚当

        【讨论】:

          【解决方案4】:

          几个简单的规则开始:

          1. 将您想要“公开”的那些声明放入您正在创建的 C 实现文件的头文件中。
          2. 仅 C 文件中的 #include 头文件是实现 C 文件所需的。
          3. 仅当头文件中的声明需要时才在头文件中包含头文件。

          4. 使用 Andrew 描述的 include guard 方法或使用 #pragma once 如果编译器支持它(它做同样的事情 - 有时更有效)

          【讨论】:

          • 这是一个很好的观点。标头 (.h) 文件应仅包含您要公开的函数。编译单元私有的任何东西(具体的实现细节)都不应该存在;将它们留在代码 (.c) 文件中。
          • 添加 xxx.h 文件应该包含在 xxx.c 中,以确保定义 (xxx.c) 和声明 (xxx.h) 一致(不一致会导致非常讨厌的错误)。如果您在相邻文件中有供公众使用的内容和其他供本地使用的内容,请务必创建 xxx.h 和 xxx-local.h。
          【解决方案5】:

          回答您的其他问题:

          This 问题促成了我的询问。这 tea.h 文件没有引用 茶.c 文件。编译器是否“知道” 每个 .h 文件都有一个对应的 .c 文件?

          编译器主要不关心头文件。编译器的每次调用都会将源 (.c) 文件编译成目标 (.o) 文件。在幕后(即在make 文件或项目文件中)正在生成与此等效的命令行:

          compiler --options tea.c
          

          源文件#includes所有它引用的资源的头文件,这是编译器找到头文件的方式。

          (我在这里忽略了一些细节。关于构建 C 项目有很多东西要学习。)

          【讨论】:

            【解决方案6】:

            除了上面提供的答案之外,将代码拆分为模块(单独的文件)的一个小优点是,如果您必须拥有任何全局变量,您可以通过使用关键词“静态”。 (您也可以将其应用于函数)。请注意,这种“静态”的使用不同于它在函数中的使用。

            【讨论】:

              【解决方案7】:

              您的问题清楚地表明您并没有真正进行太多认真的开发。通常的情况是您的代码通常太大而无法放入一个文件中。一个好的规则是您应该将功能拆分为逻辑单元(.c 文件),并且每个文件包含的内容不应超过您一次可以轻松掌握的内容。

              一个给定的软件产品通常包括来自许多不同 .c 文件的输出。通常这样做是编译器生成许多目标文件(在 unix 系统“.o”文件中,VC 生成 .obj 文件)。 “链接器”的目的是将这些目标文件组合到输出中(共享库或可执行文件)。

              通常,您的实现 (.c) 文件包含实际的可执行代码,而头文件 (.h) 在这些实现文件中包含公共函数的声明。你可以很容易地拥有比实现文件更多的头文件,有时头文件也可以包含内联代码。

              实现文件相互包含通常是很不寻常的。一个好的做法是确保每个实现文件将其关注点与其他文件分开。

              我建议您下载并查看 linux 内核的源代码。它对于 C 程序来说是相当庞大的,但可以很好地组织成不同的功能区域。

              【讨论】:

                【解决方案8】:

                .h 文件应该用于定义函数的原型。这是必要的,因此您可以在 C 文件中包含您需要的原型,而无需在一个文件中声明您需要的所有函数。

                例如,当您#include <stdio.h> 时,它提供了 printf 和其他 IO 函数的原型。这些函数的符号通常由编译器默认加载。如果您对这些文件所涉及的常规习语感兴趣,可以查看 /usr/include 下的系统 .h 文件。

                如果您只是编写功能不多的琐碎应用程序,则实际上没有必要将所有内容模块化为过程的逻辑分组。但是,如果您需要开发一个大型系统,那么您需要考虑在哪里定义每个函数。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-31
                  • 2012-03-24
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多