C 文件的组织
我习惯于在一个 C 文件中完成所有编码。 然而,我正在从事一个足够大的项目,这样做是不切实际的。 我已经将它们#include在一起,但我遇到过多次#include某些文件的情况,等等。我听说过.h文件,但我不确定它们的功能是什么(或者为什么有 2 个文件比 1) 好。
我应该使用什么策略来组织我的代码? 是否可以将特定文件的“公共”功能与“私有”功能分开?
这个问题引发了我的询问。 tea.h 文件不引用 tea.c 文件。 编译器是否“知道”每个 .h 文件都有对应的 .c 文件?
I'm used to doing all my coding in one C file. However, I'm working on a project large enough that it becomes impractical to do so. I've been #including them together but I've run into cases where I'm #including some files multiple times, etc. I've heard of .h files, but I'm not sure what their function is (or why having 2 files is better than 1).
What strategies should I use for organizing my code? Is it possible to separate "public" functions from "private" ones for a particular file?
This question precipitated my inquiry. The tea.h file makes no reference to the tea.c file. Does the compiler "know" that every .h file has a corresponding .c file?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
除了上面提供的答案之外,将代码分成模块(单独的文件)的一个小优点是,如果您必须有任何全局变量,您可以通过使用关键字 ' 将它们的范围限制为单个模块静止的'。 (您也可以将其应用于函数)。 请注意,“静态”的这种使用与其在函数内的使用不同。
As well as the answers supplied above, one small advantage of splinting up your code into modules (separate files) is that if you have to have any global variables, you can limit their scope to a single module by the use of the key word 'static'. (You could also apply this to functions). Note that this use of 'static' is different from its use inside a function.
你的问题清楚地表明你还没有真正进行过认真的开发。 通常的情况是,您的代码通常太大而无法放入一个文件中。 一个好的规则是,您应该将功能拆分为逻辑单元(.c 文件),并且每个文件所包含的内容不应超过您一次可以轻松记住的内容。
给定的软件产品通常包含许多不同 .c 文件的输出。 通常情况下,编译器会生成许多目标文件(在unix系统中为“.o”文件,VC生成.obj文件)。 “链接器”的目的是将这些目标文件组合成输出(共享库或可执行文件)。
通常,您的实现 (.c) 文件包含实际的可执行代码,而头文件 (.h) 则在这些实现文件中包含公共函数的声明。 您可以很容易地拥有比实现文件更多的头文件,有时头文件也可以包含内联代码。
实现文件相互包含通常是很不常见的。 一个好的做法是确保每个实现文件将其关注点与其他文件分开。
我建议您下载并查看 Linux 内核的源代码。 对于 C 程序来说,它相当庞大,但很好地组织成不同的功能区域。
Your question makes it clear that you haven't really done much serious development. The usual case is that your code will generally be far too large to fit into one file. A good rule is that you should split the functionality into logical units (.c files) and each file should contain no more than what you can easily hold in your head at one time.
A given software product then generally includes the output from many different .c files. How this is normally done is that the compiler produces a number of object files (in unix systems ".o" files, VC generates .obj files). It is the purpose of the "linker" to compose these object files into the output (either a shared library or executable).
Generally your implementation (.c) files contain actual executable code, while the header files (.h) have the declarations of the public functions in those implementation files. You can quite easily have more header files than there are implementation files, and sometimes header files can contain inline code as well.
It is generally quite unusual for implementation files to include each other. A good practice is to ensure that each implementation file separates its concerns from the other files.
I would recommend you download and look at the source for the linux kernel. It is quite massive for a C program, but well organised into separate areas of functionality.
.h 文件应用于定义函数的原型。 这是必要的,这样您就可以在 C 文件中包含所需的原型,而无需在一个文件中声明所需的每个函数。
例如,当您
#include
时,它提供了 printf 和其他 IO 函数的原型。 这些函数的符号通常由编译器默认加载。 如果您对与这些文件相关的常规习惯感兴趣,可以查看 /usr/include 下系统的 .h 文件。如果您只是编写功能不多的简单应用程序,则实际上没有必要将所有内容模块化为过程的逻辑分组。 但是,如果您需要开发大型系统,那么您需要考虑在哪里定义每个函数。
The .h files should be used to define the prototypes for your functions. This is necessary so you can include the prototypes that you need in your C-file without declaring every function that you need all in one file.
For instance, when you
#include <stdio.h>
, this provides the prototypes for printf and other IO functions. The symbols for these functions are normally loaded by the compiler by default. You can look at the system's .h files under /usr/include if you're interested in the normal idioms involved with these files.If you're only writing trivial applications with not many functions, it's not really necessary to modularize everything out into logical groupings of procedures. However, if you have the need to develop a large system, then you'll need to pay some consideration as to where to define each of your functions.
编译器
您可以在此主题<中查看C“模块”的示例< /a> - 请注意,有两个文件 - 头文件 tea.h 和代码 tea.c。 您可以在标头中声明您希望其他程序访问的所有公共定义、变量和函数原型。 在您的主项目中,您将 #include ,并且该代码现在可以访问标题中提到的 tea 模块的函数和变量。
之后事情会变得更复杂一些。 如果您使用 Visual Studio 和许多其他为您管理构建的 IDE,请忽略这部分 - 它们负责编译和链接对象。
链接器
当您编译两个单独的 C 文件时,编译器会生成单独的目标文件 - 因此 main.c 变为 main.o,而 tea.c 变为 tea.o。 链接器的工作是查看所有目标文件(您的 main.o 和 tea.o),并匹配引用 - 因此,当您在 main 中调用 tea 函数时,链接器会修改该调用,因此它实际上确实调用了正确的在茶中发挥作用。 链接器生成可执行文件。
有一个很棒的教程对此进行了更深入的探讨主题,包括范围和您将遇到的其他问题。
祝你好运!
-亚当
Compiler
You can see an example of a C 'module' at this topic - Note that there are two files - the header tea.h, and the code tea.c. You declare all the public defines, variables, and function prototypes that you want other programs to access in the header. In your main project you'll #include and that code can now access the functions and variables of the tea module that are mentioned in the header.
It gets a little more complex after that. If you're using Visual Studio and many other IDEs that manage your build for you, then ignore this part - they take care of compiling and linking objects.
Linker
When you compile two separate C files the compiler produces individual object files - so main.c becomes main.o, and tea.c becomes tea.o. The linker's job is to look at all the object files (your main.o and tea.o), and match up the references - so when you call a tea function in main, the linker modifies that call so it actually does call the right function in tea. The linker produces the executable file.
There is a great tutorial that goes into more depth on this subject, including scope and other issue you'll run into.
Good luck!
-Adam
尝试使每个 .c 专注于特定的功能领域。 使用相应的 .h 文件来声明这些函数。
每个 .h 文件的内容周围都应该有一个“标头”保护。 例如:
这样您就可以根据需要多次包含“accounts.h”,并且第一次在特定编译单元中看到它时将是唯一真正提取其内容的编译单元。
Try to make each .c focus on a particular area of functionality. Use the corresponding .h file to declare those functions.
Each .h file should have a 'header' guard around it's content. For example:
That way you can include "accounts.h" as many times as you want, and the first time it's seen in a particular compilation unit will be the only one that actually pulls in its content.
您应该将 .h 文件视为 .c 文件的接口文件。 每个 .c 文件代表一个具有一定功能的模块。 如果.c 文件中的函数被其他模块(即其他.c 文件)使用,则将函数原型放在.h 接口文件中。 通过将接口文件包含在您的原始模块 .c 文件以及您需要该函数的每个其他 .c 文件中,您可以使该函数可供其他模块使用。
如果您只需要某个 .c 文件中的函数(而不是任何其他模块中的函数),请将其范围声明为静态。 这意味着它只能从定义它的 c 文件中调用。
跨多个模块使用的变量也是如此。 它们应该放在头文件中,并且必须用关键字“extern”进行标记。 注意:对于函数,关键字“extern”是可选的。 函数始终被视为“外部”。
头文件中的包含防护有助于避免多次包含相同的头文件。
例如:
Module1.c:
Module1.h:
Module2.c
You should regard .h files as interface files of your .c file. Every .c file represents a module with a certain amount of functionality. If functions in a .c file are used by other modules (i.e. other .c files) put the function prototype in the .h interface file. By including the interface file in your original modules .c file and every other .c file you need the function in, you make this function available to other modules.
If you only need a function in a certain .c file (not in any other module), declare its scope static. This means it can only be called from within the c file it is defined in.
Same goes for variables that are used across multiple modules. They should go in the header file and there they have to marked with the keyword 'extern'. Note: For functions the keyword 'extern' is optional. Functions are always considered 'extern'.
The inclusion guards in header files help to not include the same header file multiple times.
For example:
Module1.c:
Module1.h:
Module2.c
从几个简单的规则开始:
仅当头文件中的声明需要时才在头文件中包含头文件。
A couple of simple rules to start:
include header files in a header file only if required for the declarations within that header file.
回答您的附加问题:
编译器主要不关心头文件。 编译器的每次调用都会将源 (.c) 文件编译为对象 (.o) 文件。 在幕后(即在
make
文件或项目文件中)正在生成与此等效的命令行:源文件
#include
包含资源的所有头文件它引用,这就是编译器查找头文件的方式。(我在这里掩盖了一些细节。关于构建 C 项目有很多东西需要学习。)
To answer your additional question:
The compiler is not primarily concerned with header files. Each invocation of the compiler compiles a source (.c) file into an object (.o) file. Behind the scenes (i.e. in the
make
file or project file) a command line equivalent to this is being generated:The source file
#include
s all the header files for the resources it references, which is how the compiler finds header files.(I'm glossing over some details here. There is a lot to learn about building C projects.)