代码之家  ›  专栏  ›  技术社区  ›  Kyle Cronin

C文件的组织

  •  28
  • Kyle Cronin  · 技术社区  · 17 年前

    我习惯于在一个C文件中编写所有代码。然而,我正在做一个足够大的项目,这样做是不切实际的。我曾经把它们放在一起,但我遇到过多次把一些文件放在一起的情况,等等。我听说过.h文件,但我不确定它们的功能是什么(或者为什么有2个文件比1个好)。

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

    This 这个问题促使我进行调查。tea.h文件没有引用tea.c文件。编译器是否“知道”每个.h文件都有一个对应的.c文件?

    8 回复  |  直到 9 年前
        1
  •  36
  •   Be Kind To New Users    11 年前

    接口文件

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

    例如:

        #include "Module1.h"
    
        static void MyLocalFunction(void);
        static unsigned int MyLocalVariable;    
        unsigned int MyExternVariable;
    
        void MyExternFunction(void)
        {
            MyLocalVariable = 1u;       
    
            /* Do something */
    
            MyLocalFunction();
        }
    
        static void MyLocalFunction(void)
        {
          /* Do something */
    
          MyExternVariable = 2u;
        }
    

    模块1.h:

        #ifndef __MODULE1.H
        #define __MODULE1.H
    
        extern unsigned int MyExternVariable;
    
        void MyExternFunction(void);      
    
        #endif
    

    模块2.c

        #include "Module.1.h"
    
        static void MyLocalFunction(void);
    
        static void MyLocalFunction(void)
        {
          MyExternVariable = 1u;
          MyExternFunction();
        }
    
        2
  •  10
  •   Andrew    17 年前

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

    每个.h文件的内容周围都应该有一个“头”保护。例如:

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

    通过这种方式,您可以根据需要多次包含“accounts.h”,并且第一次在特定编译单元中看到它时,将是唯一一次真正提取其内容的编译单元。

        3
  •  7
  •   Community Mohan Dere    9 年前

    编译程序

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

    连接器

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

    great tutorial 这将更深入地讨论这个主题,包括范围和您将遇到的其他问题。

    -亚当

        4
  •  7
  •   Community Mohan Dere    6 年前

    开始时有两条简单的规则:

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

    4. 使用Andrew描述的include guard方法或使用 如果编译器支持它(做同样的事情——有时效率更高)
        5
  •  3
  •   Community Mohan Dere    9 年前

    This 这个问题促使我进行调查。这个 tea.c文件。编译器“知道”了吗 每个.h文件都有一个对应的 .c文件?

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

    compiler --options tea.c
    

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

        6
  •  3
  •   David L Morris    17 年前

    与上面提供的答案一样,将代码分成模块(单独的文件)的一个小优点是,如果必须有任何全局变量,可以使用关键字“static”将其范围限制为单个模块。(您也可以将此应用于函数)。请注意,“static”的这种用法不同于它在函数中的用法。

        7
  •  1
  •   1800 INFORMATION    17 年前

    你的问题清楚地表明,你还没有真正做过很多认真的开发工作。通常情况下,您的代码通常太大,无法放入一个文件中。一个很好的规则是,您应该将功能划分为逻辑单元(.c文件),每个文件中包含的内容不应超过您一次可以轻松保存在头脑中的内容。

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

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

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

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

        8
  •  0
  •   hoyhoy    17 年前

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

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

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

    推荐文章