最近在学习C++,在编译和链接过程中遇到了一些定义和声明的问题。 查阅了很多地方的资料,基本解决了困惑。 我现在把这个记录下来,希望能让其他人少走一些弯路。
C++ 头文件应使用什么扩展名?
业界常用的格式如下:
总之:源文件推荐使用.cpp,头文件推荐使用.hpp。
文件方面没什么好说的。 您可以使用.cpp/.cc。 但你需要注意文件。
c的头文件格式是.h,用h表示,所以很多人在c++中也喜欢用.h作为头文件扩展名。 事实上,扩展名并不影响编译结果,并且扩展名对于编译器来说并不重要。 (您甚至可以使用.txt)。 但如果你在一个混合c和c++的大型项目中,你很难立即分辨出这是cpp文件还是ac文件; 另外,在vim或者插件的语法提示中,.h是C语言的,所以当你在c文件中写cpp的一些语法时,当然会提示不正确(的当然还是可以编译通过的)
因此,我认为最好的结果是,如果文件涉及任何C++语法,那么头文件应该以.hpp为后缀,否则将以.h为后缀
文件和文件要写什么
理论上来说,无论C++语言支持什么,都可以写入文件以及文件中的内容。 例如,如果将函数体写在文件中,只要该文件包含在任何文件中,该函数就可以编译成文件。 (编译是基于文件的,如果这个文件没有包含在任何文件中,这段代码就没用),你可以在文件中声明函数、变量和结构体,这不是问题! !
那么为什么一定要分为文件和文件呢? 为什么函数、变量声明、宏声明、结构体声明一般都在文件中完成? 文件中的变量定义和函数实现怎么样?
原因如下:
如果一个函数体是在一个文件中实现的,那么如果在多个文件中引用它,并且同时编译多个文件,生成的文件会连接成一个可执行文件,并且引用这个的每个文件的生成文件文件将是文件,有一个用于此功能的代码。 如果这个函数没有定义为本地函数,那么连接时会发现多个相同的函数,就会报错。 如果在文件中定义一个全局变量并为该全局变量分配一个初始值,那么引用该文件的多个文件中也会存在相同变量名的副本。 关键是这个变量被赋予了一个初始值,所以编译器会把这个变量放到DATA段中。 最后,在连接阶段,DATA段中会出现多个相同的变量。 它不能将这些变量统一为一个变量,即只为该变量分配一个空间,而不是多个空间。 ,假设该变量在file中没有被赋予初始值,编译器会将其放入BSS段中,链接器只会为BSS段中的多个同名变量分配一个存储空间。
如果你在一个文件中声明了宏、结构体、函数等,那么如果你想在另一个文件中引用相应的宏或结构体,就必须再次做同样的工作。 如果我更改文件中的声明,那么我就会忘记更改其他文件中的声明。 这会造成一个大问题。 如果你把这些公共的东西放在一个头文件中,想使用它的文件,只需要引用一个就可以了! 在文件中声明结构体、函数等。当你需要将你的代码封装成一个库,让别人使用你的代码,但你又不想发布源代码时,那么别人如何使用你库中的函数呢? ? 一种方法是发布源代码,其他人可以随意使用。 另一种是提供一个文件,别人可以从文件中看到你的函数原型,这样他们就知道如何调用你写的函数,就像你调用函数一样。 里面的参数是怎样的? 你怎么知道? 你不是只看别人头文件中的相关语句啊! 头文件与源文件有何关系?
已知文件ah声明了一系列函数(只有函数原型,没有函数实现),而这些函数在b.cpp中实现。 那么如果我想在c.cpp中使用ah中声明的这些,就在b.cpp中实现 c.cpp中使用的函数通常使用#“ah”,那么c.cpp如何找到b.cpp中的实现呢?
编译器预处理时,文件中必须包含#命令:将ah的全部内容复制到#“ah”。 这也解释了为什么很多编译器并不关心这个文件的后缀是什么——因为#预处理就是为了完成复制和插入代码的工作。
程序编译时,不会在b.cpp文件中查找函数实现。 这项工作仅在链接时完成。 当我们在b.cpp或c.cpp中使用#“ah”时,实际上是引入了相关语句。 ,这样编译就可以通过,程序不关心实现在哪里,如何实现。 源文件被编译成obj文件。 在这个文件中,这些函数和变量被视为符号。 连接时,需要指定需要连接哪个obj文件(这里是b.cpp生成的.obj文件)。 这时候连接器就会去.obj文件中查找b.cpp中实现的函数,然后构建到其中。 在指定的可执行文件中。
在Clion中,一般不需要自己写。 您只需包含所有必需的文件,Clion 就会自动为您编写。
通常,编译器会在每个 .o 或 .obj 文件中查找所需的符号,而不是只在某个文件中查找或找到则不查找。 因此,如果发现在几个不同的文件中链接中实现了相同的功能,或者定义了相同的全局变量,链接时就会提示。
什么是声明? 什么是定义? 必须使用; 变量不能被赋予初始值;
❝无论是定义还是声明,与位于文件还是文件中无关。
根据上述规定,我们可以得出以下结论:
extern int a; // 声明
int a; // 定义
int a = 0; // 定义
extern int a = 0; // 定义
❝许多程序员对定义变量和声明变量感到困惑。 定义变量和声明变量的区别在于定义会引起内存分配操作,这是汇编阶段的概念; while 声明仅告诉包含该声明的模块在连接阶段从该模块开始。 其他模块寻找外部函数和变量。 如何跨文件使用全局变量/全局函数?
我们在已编译模块中的任何文件中编写的变量/函数都可以在该模块中的其他文件中访问,但其他已编译模块中的文件没有访问该变量的权限。 那么如何跨模块共享变量/函数呢? 功能呢?
答案是使用。 执行此操作时请记住它的定义:它表示修改的变量或函数可能位于其他模块中。
❝请务必牢记上述定义。 有了定义,我们可以思考以下问题
❝
全局变量
这样,当我们编译某个单元时,编译器就会找到被修改的变量。 如果该模块中有相关定义,则直接使用; 如果没有相关定义,那么在编译其他后续模块时就会挂掉。 搜索,如果最后没有找到,那么链接阶段会报错ld: (s) not found for;
正确的做法是在test1.hpp中声明int a; 定义 int a = 10; 在test1.cpp中(或者使用int a;来定义,此时值为默认值0)在test2.cpp中#“test1.hpp”,这样就可以直接在test2.cpp中使用a变量了。 错误方法1
在头文件test1.hpp中直接int a = 10;
这是直接在头文件中定义的。 我们已经说过,一个变量可以在多个地方声明,但只能在一处定义。 这种情况下,如果有多个文件#“test1.hpp”,就会导致在obj文件的链接阶段,发现多个地方存在同一个变量的定义。 此时会报错ld: 1 for。
❝同时,在头文件中定义变量是一种非常业余的方法。 请不要急于模仿错误的方式2
直接使用int a = 10; 在头文件test1.hpp中,直接使用int a; 在 test2.cpp 中(没有 # test1.hpp)
这样做可以避免多处重复定义的问题,但这种情况下test1.hpp中定义的其他变量和方法就无法使用了。 它们都必须以***的形式声明然后使用。 这将超过得失。
总结
所以我们可以得出结论:
真理总是那么简单!
全局函数
函数和变量类似,也分为定义和声明。 但是,与声明时必须包含的变量不同,函数的定义和声明之间存在差异。 定义函数必须有函数体,而声明函数则没有函数体,所以函数定义声明时可以省略。 反正其他文件也知道这个函数是在别处定义的,所以不加也没关系。
❝所以在cpp中,如果添加在某个函数之前,仅表示该函数可能定义在其他模块中; 或者当我们只使用某个头文件的这个方法时,它也可以允许我们不使用#来使用它。 用它来修改类中的变量/函数
是静态成员变量/函数
当用于修改类外部的变量/函数时
是一个普通的全局静态成员变量/函数
使变量仅在此编译模块中可见。 在这种情况下,如果两个编译模块各有一个值变量,那么就不要认为这两个编译模块中修改的变量是同一个内存。 它们实际上是两份副本。 内存,修改一个不会影响另一个
目标范围是已编译的模块。 怎么理解呢? 常量
const 单独使用时,与 相同,与 一起使用时,其特性与
用途及意义
# 这样可以保证你的头文件在这个编译的模块中只被编译一次(但是如果多个模块编译这段代码,仍然会出现重复的代码)
总结一些头文件以及文件中定义的声明和规则就是模块接口的声明。 接口包括模块提供的外部函数和外部全局变量,供其他模块调用。 这些变量和函数需要在文件中命名为key。 模块中的函数和全局变量必须在文件开头用关键字声明。 切勿在文件中定义变量。 如果你想使用其他模块定义的变量和函数,只需 # 它们的文件。 ❝ 如果项目很大,启动文件很多,经常会用到几个头文件,那么
❝将这些头文件全部写到一个文件中,例如写一个preh.h,写一个preh.cpp。 里面只有一句话:#“preh.h”对于preh.c,在里面设置,对于其他c++文件,设置use file
参考