c 包含其他文件_C/C++编程笔记:C/C++的编译和链接,计算机专业大学生必备知识...

9b446503d6bff28c843164ca92d1bcdf.png

C/C++文件

C/C++程序文件包括 .h .c .hpp .cpp,其中源文件(.c .cpp)是基本的编译单元,头文件(.h .hpp)不会被编译器编译。

C/C++项目构建(build)过程,分为以下几个步骤 预处理 → 编译 → 链接。

预编译

预编译的过程可以理解为编译器(实际上是预处理器,这里统称为编译器就可以了)在正式编译之前处理C/C++文件中的预处理命令,即#开头的代码。

常用的几个预处理命令如下:

#include ......

#ifdef ...... #else......#endif

#define ......

#pragma ......

举个例子,下面是个很简单的类定义:

MyClass.h

e7510082d0f4d6b1b288d243b532309b.png

MyClass.cpp

96cc6f0463b065ba0ccd9a51eeb12f11.png

预编译完成后的样子:

2072c859b9d84d94cfd1fd24a814e846.png

可以看到编译器把.h文件替换到了.cpp文件中的#include 位置上,把DEFAULT_VALUE定义的值也替换到了相应的位置。

编译

预编译之后,编译器会编译每个源文件(.c .cpp),如果编译成功,会生成对应的目标文件,Linux为.o文件,Windows平台下为.obj文件。

以Linux平台为例,上面的MyClass.cpp编译完成后会生成MyClass.o文件

使用objdump可以看到目标文件MyClass.o的内容

8feb1852d01cf8ca1af2c2f389f2e987.png

编译器会把MyClass::Fun()的名字改成_ZN7MyClass3FunEv,这个过程叫Mangle,由于C++支持重载,覆盖等特性,所以编译器必须把函数用一个唯一的标识表示。这个字符串就是编译器生成的唯一标识。

这里还要单独说一下头文件,头文件的既然不是编译单元,那么它的作用是什么?

头文件就是负责”声明“,编译器在编译MyClass.cpp的时候,对于MyClass这个类以及Fun()这个成员函数,编译器必须找到它的声明,这个函数才能被正确编译。

如果有其他cpp需要使用MyClass这个类的时候,也需要它的的声明。例如

main.cpp

bb4f6d9d482e9685e8f43218931e47f5.png

加上#include "MyClass.h" 编译器在编译main.cpp的时候才知道怎么编译MyClass这个类。MyClass.h里声明是不会真正被编译到main.o中,.h文件中的内容在目标文件中只是以列表的形式存在,这个表在后面链接时会用到。

当然,头文件不仅可以用来声明,还可以定义(定义全局变量,全局函数等),在头文件中的定义要小心,可能会引起链接错误。

链接

链接就是将一堆目标文件加静态库文件装配成可执行文件的过程。(或者是装配成静态/动态库的过程)

上面两个cpp分别被编译成了MyClass.o, 和main.o,我们要生成可执行程序的话,就必须经过链接的过程,把两个目标文件合成一个可执行文件。

main.o中,main函数会构造MyClass, 并且调用Fun()函数,那么main就根据MyClass.h生成的表,找到MyClass.o中的函数,这个就是链接器要做的工作。

常见错误

构建c/c++工程的时候,最常见的就是两种错误:

-- 编译错误,在编译过程中产生的错误,通常是语法错误,没有声明,重复声明导致编译目标文件错误

其中没有声明通常是由于没有#include相应的头文件,或者头文件缺少相应的声明。

而重复声明通常是#include了相同的头文件,比如 B.h 和 C.h 都包含 A.h,然后 main.h 包含了 B.h 和 C.h,这就导致A.h 在main中被包含了两次。

解决这个问题的方法是可以在所有.h文件的第一行加上

#pragmaonce

或者,使用#ifndef ... #define ... #endif 语句块

e1aa73f0866127816c93ce87fe4e8792.png

-- 链接错误,常见的错误也是两种,没有定义和重复定义,和上面的没有声明,重复声明类似。(这里定义指的就是函数实现)

先讨论没有定义(undefined reference to xxx)

通常是因为函数有声明,而且被使用了,但是没有被定义。比如上面MyClass.cpp中,如果Fun()没有被实现的话,MyClass.cpp和main.cpp编译时都不会报错,但是链接时会报告找不到Fun()。

当然,如果Fun()没被main.cpp调用的话,即使不实现它,整个构建过程也不会出错,因为链接器根本不会去找这个函数的定义。

然后是重复定义(multiple definition)

指的一份相同的定义在两个目标文件中都存在,链接的时候链接器不知道时用哪个了。这种问题通常由于全局函数,和全局变量定义在了头文件中。导致多个目标文件包含相同的全局函数和全局变量的定义。

解决方法就是在头文件中声明,定义放到cpp文件中,或者为定义加上const 或 static这样的修饰符,链接时会对这些带有const和修饰符的变量特殊处理的。

const只适用于定义常量变量,static定义的是静态全局变量,只在当前cpp有效,所以链接它也不会被别的目标文件链接,就不会有重复定义的问题了。

总之在头文件中定义变量和函数要特别主意,可能会导致链接错误。

当然也不是所有定义都不能放到头文件中,比如刚才说的const常量,static全局变量就是例外,还有内联函数,可以定义在.h文件中,因为内联函数会被拷贝到每个目标文件中,也不会参与链接的过程。

还有模板类必须放在头文件中定义,这个下面会讨论这个。

关于模板,静态成员变量

模板类模板函数必须声明和定义在头文件中,原因是什么,举个例子,假设MyClass是模板类

MyClass.h

23099a683a5cc76791161d6204da69c5.png

MyClass.cpp

e0224100934d321134364fcacfc9397a.png

main.cpp

8af88f8adf784f70ab5989956949e6e5.png

编译的时候没有问题,但是链接时会报错,main.cpp找不到MyClass::Fun(),如下图

9bb61d07c44dbcff3bd1a24056451773.png

MyClass虽然定义了Fun函数,但是MyClass.o中存在MyClass::Fun(),而根据MyClass.h文件,main.o中需要找到MyClass::Fun()的定义

结果链接器哪都找不到,只好报错了。(实际上通过objdump查看MyClass.o,编译器都没有生成MyClass::Fun(),因为编译器认为这个函数没人使用,就直接优化掉了)

如果非得在cpp中定义模板类的成员函数呢,有一种方法就是要显示的在cpp文件中声明,比如

MyClass.cpp

c1e6e482a95ae7fdc05d9352202d16be.png

加上下面这行就不会有问题了,但是缺点就是开发MyClass的程序员无从知道其他类是怎么使用这个模板的,不可能把所有可能的模板参数全都一一的列在这里。

所以模板类的定义还是要写在.h文件中,

那么如果main.cpp使用到了MyClass, 另外一个cpp也使用到了MyClass,会不会产生重复代码导致重复定义呢,不会,编译器会处理好模板类的。

下面是静态成员变量,为什么静态成员变量的定义要放在cpp里,(模板类的静态成员变量除外)

静态成员变量和静态全局成员变量不同。

静态成员变量的作用域可以是整个工程,而静态全局变量的作用域只是当前的cpp。所以静态成员变量定义在.h中就会发生重定义错误。

想要在程序员生涯内有更高的成就的话,C/C++就是一个既可以强化思维能力,又可以打好编程基础的编程语言,你想要做软件开发,成为核心程序员的话,学习C/C++的话笔者有一个C/C++的编程俩千人羣(Q艘索:C/C++编程学习13群)你如果感觉自学C/C++语言有困难的话,有兴趣学习或者了解一下C/C++编程的小伙伴就可以进来交流。

c6bae20d92a5c9cb1087557848e54b44.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python函数命名空间_Python中的函数 ​命名空间 作用域和局部函数 匿名(lambda)函数...

函数 是 Python中最重要的代码组织和复用手段函数用def关键字声明,return关键字返回: def function(x, y, z1.5): if z > 1: return z * (x y) else: return z / (x y) ##可以拥有多条return语句,如果到达函数末尾时没有遇到任何一条ret…

二建施工管理思维导图_备考二建不丢分?二建思维导图全程指导,知识点记忆快、不分散...

关键字:二建 一建 建造师考试 建造师证书 建筑 工程 建筑项目 法规 管理 市政 公路目前,距离2020年二级建造师考试的时间越来越近,很多准备参加二建考试的朋友也开始紧张起来了。二建考试分为公共科和专业科,公共科为法规和管理&a…

机器学习线性回归算法实验报告_吴恩达机器学习系列4:线性回归的梯度下降算法...

之前我们已经学过了线性回归、代价函数和梯度下降,但是他们就像一个人的胳膊和腿,只有组合在一起才会成为一个「完整的人」,这个「完整的人」就是一个机器学习算法,让我们一起来学习第一个机器学习算法吧。这个机器学习算法叫做线…

树莓派python3_【树莓派】给ubuntu18安装python3.7

准备工作 安装工具 sudo apt update sudo apt upgrade sudo apt install gcc sudo apt install g sudo apt-get install libffi-dev sudo apt install build-essential checkinstall sudo apt install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-de…

c++mfc编写实验_零基础学Windows窗口图形界面编程(不用MFC),菜鸟学完变身高手,敢与专业媲美...

又一门新的计算机课上线啦!什么课?看下面视频(以下视频是本课第一讲)用什么语言我们声明一下(这个在我们课上也跟同学强调):不要为用什么语言掐架!那是初学者的行为,为高手所不为。第二,我们这门课讲的不是…

超级计算机阿波罗11,Apollo 8000推进超算科学发展

Apollo 8000推进超算科学发展超级计算技术将理论转移到模拟数字环境和计算机分析,一直被应用于加快科学和工程领域的突破。最新的高性能计算技术带来的创新让企业客户也能够访问这些程序和应用、强化研发能力并获得竞争优势。Apollo 8000推进超算科学发展作为散热媒…

找不到r低版本_R的多进程使用与改进

R的多进程使用与改进在R中需要使用多进程时,常见方案是使用foreach和doParallel的组合。foreachforeach包中最重要的是foreach函数,该函数创建一个foreach对象,随后串行或并行的执行表达式。library(foreach)?foreachout:foreach( ..., .c…

html让图片移动到一定位置_百度移动搜索优化指南2.0

百度移动搜索优化指南 2.0前期准备工作【域名】与 PC 网站一样,域名是用户对一个网站的第一印象。一个好的移动域名,不仅容易记忆、易于输入,还能方便用户向其他人推荐。域名应尽量简短易懂,越短的域名记忆成本越低,越…

32位mysql安装包_关于Mysql的安装

在安装之前,为保证能够安装成功,请您尝试以下操作:1、卸载原有mysql;2、搜索C:盘中是否有残余的mysql文件,主要是log文件,全部删除;3、为保证能够正常连接服务器,尽量关闭电脑防火墙&#xff1b…

山东初二计算机会考,2017山东莱芜初二会考科目时间安排:6月14日

考试科目初中学业考试科目、分值和考试时长分别为:语文、数学、英语三科满分均为120分,考试时长均为120分钟,均按原始得分计入总分;思想品德、化学、历史、地理、生物满分均为100分,考试时长均为90分钟,均按考试分数50…

安装 ubuntu18_Ubuntu18.04安装docker及nvidia docker

Ubuntu18.04安装docker及nvidia docker 2之前的时候记得安装完docker之后还需要安装单独的nvidia docker 2,现在的话只需要安装nvidia container toolkit即可1、docker安装官网上又详细的介绍Install Docker Engine on Ubuntu​docs.docker.com或者sudo apt-get upd…

google账号解除游戏绑定_成长守护平台解除实名认证 公众号解绑操作流程

微信成长守护平台是一款培养孩子健康游戏习惯的游戏管控工具,那么微信成长守护平台怎么解除王者荣耀防沉迷呢?接下来小编就给大家带来了解除实名认证方法介绍,一起来看看吧!先简单了解一下游戏成长守护平台,它的运作方…

混凝土地坪机器人_地面整平机器人:精准又高效,轻松摆“平”混凝土

随着国内经济不断发展,工业厂房、大型广场、体育场、停车场等大面积场地的建设需求越来越多。这类场地大多使用混凝土现场浇筑地基,然后再覆盖地砖或地坪漆,因此对基础层的平整度提出了很高的要求。传统的混凝土地坪施工方法是人工找平&#…

大学计算机成绩统计表怎么做,wps怎么制作成绩表 wps设计成绩统计表的步骤方法...

当我们使用wps统计数据时,如果我们需要制作一个统计成绩的表格,可是怎么制作成绩表吗?其实方法非常的简单,下面教程之家网为大家分享wps设计成绩统计表的步骤方法,不会制作的朋友可以参考下面的步骤方法自己制作即可。wps怎么制作…

chackbox的值 php获取_最详细最全的PHP面试题(附答案)

这篇文章介绍的内容是关于最全最详细的PHP面试题(带有答案),有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下相关推荐:程序猿的生活:面试12家公司,收获9个offer,2020年PHP 面试问题…

python适合安装什么开发环境_python开发环境安装

1、首先安装python-3.4.2.msi,此为python主程序,双击安装,根据自身的情况做选择,也可以使用默认设置,一路next也没什么问题。 2、设置环境变量>编辑Path,在最后加上你的python安装路径。我的安装路径是&…

服务器iis的作用,IIS是什么 IIS服务组件有什么作用

在网上或者一些技术社区我们经常会遇到一些讨论IIS问题文章。那么究竟IIS是什么?有什么用呢?不少电脑爱好者对IIS都充满兴趣,以下脚本之家小编为大家简单介绍下。IIS是什么?IIS是Internet Information Services英文全称的缩写,是…

服务器ssr进程启动怎么运行,要SSR? NUXT项目从初始化到部署服务器流程全记录

首先,简单介绍一下, nuxt.js 是什么Nuxt.js 是一个 Vue.js的SSR通用框架,最常用的就是用来作SSR(服务器端渲染)。首先说下 SSR,最近很热的词,意为 Server Side Rendering(服务端渲染),目的是为了解决单页面应用的 SEO 优化 的问题…

4修改初始值_Java基础语法 --4(连载)

第一部分 : IDEA开发工具参见 :IEDA的安装请参考文件夹PPT中的 04_IDEA.ppt1.数组1.1 数组介绍​ 数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致。 1.2 数组的定义格式1.2.1 第一种格式​ 数据类型[] 数组名​ 示例:int[] ar…

python字典常用方法_Python 字典的常见操作

字典 字典的增删改查 字典的创建方式: #创建字典类型 info {name:李白,age:25,sex:男} msg{user01:Longzeluola,user02:xiaozemaliya,user03:cangjingkong} 字典存储数据的时候,采取的是key:value的形式,是一组无序的存储。在需要的时候通过k…