C/C++ 的基本结构项目(头文件和cpp文件)
这是一个脑死亡的新手问题,但这里是:
什么决定了 C/C++ 项目中包含哪些文件?
我的理解是,编译器从包含 main() 的文件开始,该文件将包含 #include 来获取各种 h 文件,其中包含其他 h 文件的 #include 等,直到所有内容都包含在项目中。
我的问题:
同名的h文件和cpp文件有什么关系?我的意思是,当然我理解代码方面它们需要彼此,并且 cpp 文件总是(几乎总是?)#include 是 h 文件,但从编译器的角度来看,它们具有相同的名称很重要,或者是这一切只是一个约定?我可以包含额外的 cpp 文件而不包含相应的 h 文件吗?
另外,当项目构建并链接时,它如何知道要为哪些 cpp/h 文件构建目标文件?它会从包含“main()”的 cpp 文件开始,并继续执行 #include 直到它拥有所需的所有内容并构建所有这些内容,还是仅构建用户在 makefile 或 makefile 中指定的所有内容IDE项目文件?
最后,当链接器最终出现并链接所有目标代码以生成可执行文件时,它是否有特殊的顺序来排列所有内容?
任何帮助、提示、解释表示赞赏.. 谢谢!
--R
This is a brain-dead newbie question, but here goes:
What determines what files get included in a C/C++ project?
My understanding is that the compiler starts with the file that has main() in it and that file will contain #include's to get various h files which contain #include's to other h files and so on until everything is included in the project.
My questions:
What is the relationship between h files and cpp files of the same name? I mean, of course I understand that code-wise they need each other and the cpp file always (almost always?) #include's the h file, but from the compiler's point of view is it important for them to have the same names or is this all just a convention? Can I include extra cpp files without corresponding h files?
Also, when the project is built and linked, how does it know which cpp/h files to build object files for? Will it just start at the cpp file with "main()" in it and keep going through #include's until it has everything it needs and build all of that, or does it just build everything that a user specifies in the makefile or in the IDE project file?
Finally, when the linker finally comes around and links all the object code to make an executable, is there a special order it arranges everything in?
Any help, hints, explanations appreciated..
Thanks!
--R
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
将文件视为拆分代码以使其更可重用且更易于维护的简单方法。
您可以轻松地将整个应用程序放入一个大的源文件中,但您可能会发现该文件会变得相当大,导致编译器抱怨它(或者至少需要很长时间来编译它)。
通常,您会将应用程序的一部分(例如通用数据库访问层)分离到单独的源文件(例如
db.cpp
)中,并创建一个db.h
文件及其 API。db.cpp
使用的不多,因为需要调用db.cpp
中函数的所有其他文件都使用该文件。它可以包含在db.cpp
中,但它往往主要是有关db
代码的已发布信息。至于环境如何确定要编译/链接的内容:您往往有某种类型的项目(makefile、IDE 项目文件等),其中列出了您想要编译的所有程序(通常不是头文件)。
环境将编译每个被告知的源文件以生成目标文件 - 此过程的一部分是将包含的头文件合并到每个源文件中,以生成编译或翻译单元 - 该单元基本上是带有包含的头文件合并在
#include
所在的位置。然后,环境将链接所有目标文件以形成可执行文件。请记住,此过程存在多种变化,例如后期(动态)链接。请参阅此处了解相关说明。
Think of files as just an easy way to split up your code to make it both more reusable and more maintainable.
You can just as easily put an entire application in one big honking source file but you may find that the file will get rather big, leading to the compiler complaining about it (or at least taking a long time to compile it).
Typically you would hive off a part of your application (such as a generic database access layer) into a separate source file such as
db.cpp
and create adb.h
file with its API. This file isn't so much used bydb.cpp
as it is used by all the other files that need to call the functions indb.cpp
. It can be included indb.cpp
but it tends to be mostly published information about thedb
code.As to how an environment figures out which things to compile/link: you tend to have a project of some sort (makefile, IDE project file, etc) which lists all the programs that you want to compile (usually not header files).
The environment will compile each source file that it has been told about to produce an object file - part of this process is incorporating the included header files into each source file, to make a compilation or translation unit - this unit is basically the source file with the included header files incorporated at the point where the
#include
was.The environment will then link all the object files to form an executable. Keep in mind there are variations on this process such as late (dynamic) linking. See here for a description of this.
在网络上稍微搜索一下就会得到很多答案。这里只有两个: http://www.psgd.org/paul/docs/cstyle /cstyle02.htm
http:// www.cs.utexas.edu/~lavender/courses/EE360C/lectures/lecture-02.pdf
第二个相当不错。
我还推荐 c++ 编程语言第三版。有一个关于文件组织的很棒的部分。
至于编译器的作用,最好在另一篇文章中进行解释。简而言之,每个 cpp 文件都被编译成一个翻译单元(目标代码),然后链接器将所有内容连接到最终的可执行文件中。
A little hunting on the web will turn out a lot of your answer. Here's just two: http://www.psgd.org/paul/docs/cstyle/cstyle02.htm
http://www.cs.utexas.edu/~lavender/courses/EE360C/lectures/lecture-02.pdf
The second one is pretty good.
I'd also recommend the c++ Programming Language 3rd edition. There's a great section about file organization.
As for what the compiler does, that too is best explained in a separate article. In short, each cpp file is compiled into a translation unit (object code), then the linker connects everything together into the final executable.
头文件本质上是类及其所有成员属性和函数的前向声明,这基本上会使您的类更可重用且更易于访问。将其视为一个无需实现的接口,因此无论谁使用它都不必担心该特定类的源。据我所知,相应的 h 文件和 cpp 文件需要同名。 cpp 文件并不总是需要有相应的 h 文件,您可以将所有源代码放在一个 cpp 文件中,而无需任何 h 文件,只要一切都正确实现并正确原型化,一切都应该正常工作。
A header file is essentially a forward declaration of a class and all its member attributes and functions, this would basically make your class more reusable and more accessible. Think of it as an interface free from implementation therefore whoever is using it need not worry about the source for that particular class. From what I know corresponding h files and cpp files need to be of the same name. A cpp file need not always have a corresponding h file, you can have all your source in one cpp file without any h files and as long as everything is implemented properly and prototyped properly everything should work fine.
您的分析基本上是正确的......所有包含的文件都被扩展到位,并且生成的代码(翻译单元)被编译成对象、库或应用程序。
尽管如此,任何重要的项目都依赖于其他库中定义的符号(变量、函数),即使只是语言或操作系统提供的 malloc()、socket()、file()、write() 等。标准库。即使您不直接调用它们,像 new 和 iostream 这样的东西的实现也需要它们。
当您自己的项目变得更大时,您还需要将功能划分为不同的对象或库,因为这使得功能更具可重用性、可独立测试性,并且意味着在进行一些代码更改后,您只能重新编译那些因以下原因而无效的对象:更改然后重新链接 - 这比重新编译整个项目中的每一个代码要快得多。
您的 C++ 编译器从翻译单元创建对象(可能有也可能没有额外的接口和代码来使它们成为库或应用程序) - 这些对象是您提到的包含和 cpp 文件的串联 - 可能导入并将其与现有静态库或您在编译器命令行中提到的其他对象中的符号组合。
对于每个独立对象,编译器需要能够告诉新代码如何访问和使用所包含的符号;头文件用于此目的,广告可用的对象内容。
实现(cpp)文件几乎总是首先包含它们的头文件,因为如果它正在构建的对象内容与使用该对象的代码稍后期望的头文件广告内容之间存在一些差异,编译器就会抱怨。对于某些事物(例如类),必须在指定成员函数实现之前查看类声明,并且考虑到客户端代码需要类声明,因此在标头中,实际上实现也需要包含标头。 (我说 cpp 应该首先包含它的标头,因为如果标头依赖于它本身不包含的某些内容,编译器会抱怨。否则,如果说 cpp 包含 std::string header 和 header 使用它,但其他一些客户端代码尝试包含 header 而没有包含字符串,那么编译将失败)。
实现文件可以包含其他实现文件,但这不符合上述编译的一般划分,因此会使习惯此约定的人感到困惑。
Your analysis is basically correct... all the included files are expanded in place and the resultant code - a translation unit - is compiled into an object, library or application.
Still, any non-trivial projects relies on symbols (variables, functions) defined in other libraries, even if only for things like malloc(), socket(), file(), write() etc. provided by the language's or operating system's standard libraries. Even if you don't call them directly, they're needed by the implementation of things like new and iostream.
When your own project gets bigger, you'll also want to divide your functionality into different objects or libraries, as that makes the functionality more re-usable, independently testable, and means that after some code change you can recompile only those objects invalidated by the change then relink - which can be massively faster than recompiling every single bit of code in your entire project.
Your C++ compiler creates objects (which may or may not have the extra interfacing and code to make them libraries or applications) from translation units - which are the concatenations of includes and cpp file you've mentioned - possibly importing and combining that with symbols from existing static libraries or other objects you've mentioned on the compiler command line.
For each of these independent objects, the compiler needs to be able to tell new code how to access and use the contained symbols; the header files serve this purpose, advertising the available object content.
Implementation (cpp) files should almost always include their header file first because the compiler will then complain if there is some discrepency between the object content it is building and the header-file-advertised content that code using the object will later expect. For some things - like classes, a class declaration must be seen before the member function implementation can be specified, and given the class declaration is needed by client code and therefore in the header, in practice the implementation needs to include the header too. (I say a cpp should include its header first because the compiler will then complain if the header relies on some content that it doesn't include itself. Otherwise, if say the cpp includes the std::string header and the header uses it, but some other client code tries to include the header without having included string, then the compilation will fail).
Implementation files could include other implementation files, but that wouldn't fit in with the general division of compilation described above, so would confuse people used to this convention.