发现每次写技术博客时,都会在文章开头处花费一番功夫

...从前,有一个程序员....他的名字叫magicsoar

为什么有时会出现aaa已在bbb中重定义的错误?

为什么有时会出现无法解析的外部符号?

为什么有的内联函数的定义需要写在头文件中?

为什么对于模板,声明和定义都要写在一起?

读完这篇博客,相信你会有一个初步的认识

注,我们现在谈的编译其实可以认为由4个环节组成,其中有编译环节,链接环节, 我会尽量在上下文中指明说的总体的编译,还是具体的编译环节,望读者周知

关于编译过程详解说明,可以参照我之前的一篇博客 C++编译与链接(1)-编译与链接过程

编译单元

首先让我们来认识一下编译单元,什么是编译单元呢?简单来说一个cpp文件就是一个编译单元。

在集成式的IDE中,我们往往点击一下运行便可以了,编译的所有工作都交给了IDE去处理,往往忽略了其中的内部流程

事实上编译每个编译单元(.cpp)时是相互独立的,即每个cpp文件之间是不知道对方的存在的(不考虑#include “xxx.cpp" 这种奇葩的写法)

编译器会分别将每个编译单元(.cpp)进行编译,生成相应的obj文件

然后链接器会将所有的obj文件进行链接,生成最终可执行文件

内部链接与外部链接

那么什么内部链接和外部链接又是什么呢?

我们知道C++中声明和定义是可以分开的

例如在vs中,我们可以一个函数声明定义放在b.cpp中,在a.cpp只需再声明一下这个函数,就可以在a.cpp中使用这个函数了

a.cpp

void show();int main()
{show();
return 0;
}

b.cpp

#include <iostream>
void show()
{std::cout << "Hello" << std::endl;
}

而通过之前的了解,我们知道每个编译单元间是相互独立不知道彼此的存在的

那么a.cpp又是如何知道show函数的定义的呢

其实在编译一个编译单元(.cpp)生成相应的obj文件过程中

编译器会将分析这个编译单元(.cpp)

将其所能提供给其他编译单元(.cpp)使用的函数,变量定义记录下来。

而将自己缺少的函数,变量的定义也记录下来。

所以可以认为a.obj和b.obj记录了以下的信息

然后在链接器连接的时候就会知道a.obj需要show函数定义,而b.obj中恰好提供了show函数的定义,通过链接,在最终的可执行文件中我们能看到show函数的运行

哪这些又和内部链接,外部链接有什么关系呢?

那些编译单元(.cpp)中能向其他编译单元(.cpp)展示,提供其定义,让其他编译单元(.cpp)使用的的函数,变量就是外部链接,例如全局变量

而那些编译单元(.cpp)中不能向其他编译单元(.cpp)展示,提供其定义的函数,变量就是内部链接,例如static函数,inline函数等

好了让我们看下编译单元,内部链接和外部链接比较正式的定义吧

编译单元:当一个c或cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有 必要信息的单个源文件,这个源文件就是一个编译单元。

内部连接:如果一个名称对编译单元(.cpp)来说是局部的,在链接的时候其他的编译单元无法链接到它且不会与其它编译单元(.cpp)中的同样的名称相冲突。

外部连接:如果一个名称对编译单元(.cpp)来说不是局部的,而在链接的时候其他的编译单元可以访问它,也就是说它可以和别的编译单元交互。

最后让我们回到文章开头处的那几个问题吧

为什么有时会出现aaa已在bbb中重定义的错误?

答:你可能在不同的cpp中重复定义了一个具有外部链接的函数或变量,链接器在链接时找到了多个一样的函数或变量定义

为什么有时会出现无法解析的外部符号?

答:你可能只提供了函数或变量的声明,没有提供其定义,或者声明和定义的函数原型不一致,链接器没有找到其定义在哪里,所以在链接环节出现了无法解析的外部符号的错误

为什么有的内联函数的定义需要写在头文件中呢?

答:因为内链函数是内部链接的,如果你在b.cpp中定义这个函数,那么在a.cpp中即使有这个函数声明,但由于内链函数是内部链接的,所以b.cpp不会提供其定义

所以在链接时a.obj无法找到这个函数的定义,便会出现无法解析的外部符号的错误

为什么对于模板,声明和定义都要写在一起呢?

答:我们假设我们有如下结构的代码

b.h

#pragma once
template<typename T>
class A
{
public:A(const T &t);
};

b.cpp

#include "b.h"
#include <iostream>template<typename T>
A<T>::A(const T &t)
{std::cout << t << std::endl;
}

a.cpp

#include "b.h"int main()
{//A<int> a(5);return 0;
}

那么a.cpp中注释的那行代码能否正常运行呢?答案是不能我们首先来分析一下编译器在编译a.cpp时,发现其缺少A<int>::a(const int& t)的定义而在编译器编译b.cpp时,由于每个编译单元是独立的,而模板只有被用到的时候才会被实例化,产生定义,b.cpp不知道a.cpp用了A<int>::a(const int& t),所以它不会提供A<int>::a(const int& t)的定义,编译器不会有任何反应,这样在链接时a.obj无法找到A<int>::a(const int& t)的定义,就会出现无法解析的外部符号的错误

宏是内部链接还是外部链接

答:都不是,宏在预处理环节时就被替换掉了,而内部链接与外部链接是针对编译环节与链接环节而言的

转载于:https://www.cnblogs.com/magicsoar/p/3840682.html

C++编译与链接(2)-浅谈内部链接与外部链接相关推荐

  1. 浅谈pnpm (软链接 与 硬链接)

    概述: 前端常见包管理工具:yarn.npm.pnpm,本文主要浅谈三种包管理工具的优劣与pnpm的原理与使用. pnpm官网:https://pnpm.io/zh/motivation 一.npm. ...

  2. [激励机制]浅谈内部竞争——如何让你的员工玩命干活?

    我是标题党,标题是故意气你的,千万表拍我. 公元2012年12月12号,Clark 拿出所有积蓄创办了一个公司,招了看上去还不错的5个员工组成了一个小型团队.紧接着,摆在他面前的一个很明显的问题就是- ...

  3. java反编译工具gd gson,浅谈Android中static修饰符,及Gson转String实例

    肯定会很奇怪,为什么要把Gson和Static放一起去讲,因为在开发中,json这个东西是很常用的,不管是格式,还是数据的传递,还是我们与后台之间的交互,都离不开JSON,而今天,是在帮一个同行用ts ...

  4. 编译原理代码生成器java_浅谈JVM编译原理-.java文件转变为.class文件的过程

    为什么需要编译? 我们平常写代码,有规范的命名方式,都能够看得懂,但是我们写的代码计算机是看不懂的,所以需要编译,也就是一个转换的过程,如下: 1.这个是咱们平时写的代码,就比较好理解,对人友好 2. ...

  5. 浅谈linux中的硬链接和软链接文件以及ln的使用方法

    在linux系统中有一种文件叫做链接文件,可以为解决文件的共享使用.链接的方式可以分为两种,一种是硬链接(Hard Link),另一种是软链接或者也称为符号链接(Symbolic Link). 一.基 ...

  6. C++中头文件(.h)和源文件(.cpp)都应该写些什么,头文件中的预编译语句作用,命名空间和头文件的区别与联系,内部链接和外部链接

    1. C++中头文件(.h)和源文件(.cpp)都应该写些什么: 总结下来就是头文件写的就是类的声明(包括类里面的成员和方法的声明)和函数的声明,但一般来说不写出具体的实现.对应的同名(可以不同名,但 ...

  7. 浅谈Ddos攻击攻击与防御

    EMail: jianxin#80sec.com Site: http://www.80sec.com Date: 2011-2-10 From: http://www.80sec.com/ [ 目录 ...

  8. 浅谈运维工程师的开发能力的培养

    写在前面 本文已获得作者授权,作者的博客地址:https://www.cuiliangblog.cn/ 原文链接:浅谈运维工程师的开发能力的培养 一.运维工程师发展路线 1. 传统运维 侧重点是解决具 ...

  9. 浅谈XS-Leaks之Timeless timing attck

    本文首发于先知社区,转载请联系对方 原文链接:浅谈XS-Leaks之Timeless timing attck 文章目录 1 XS-Leaks简介 1 什么是XS-Leaks? 2 XS-Leaks和 ...

  10. 浅谈软件定义网络(SDN)技术研究现状和发展趋势

                       浅谈软件定义网络(SDN)技术研究现状和发展趋势 友情全文PDF链接:浅谈软件定义网络(SDN)技术研究现状和发展趋势.pdf-网络基础文档类资源-CSDN下载 ...

最新文章

  1. 开始位置 环状图_消防泵房内设备、管网、阀门的设置及系统图
  2. linux批量安装 五大开源软件挨个看,51CTO_Linux运维和管理:自动化监测.pdf
  3. python如何删除代码_Python如何删除除字母和数字之外的所有字符?(代码示例)
  4. java集合——java.util.Properties类
  5. 初识Firebug 全文 — firebug的使用
  6. IDEA连接PostgreSQL数据库
  7. oracle wm_concat listagg,oracle分析函数:四、listagg和wmsys.wm_concat
  8. testng_TestNG注释
  9. 怎样将short[]数组转换成byte[]数组
  10. 找出回文字符串用c语言写,寻找回文字符串
  11. 错误: 在类中找不到 main 方法, 请将 main 方法定义为: public static void main(String[] args) 否则
  12. x264码率控制介绍、配置及应用
  13. java8-Stream集合操作学习
  14. 字节、快手、天弘基金等 :量化/算法工程师岗位【社招|校招|实习生】
  15. 黑马程序员---java算法实现输出任意奇数维数独
  16. 利用matplotlib制作饼图
  17. 关于推特社交机器人数据集(来自上一篇论文)
  18. cad 打开硬件加速卡_CAD经常性卡顿?要怎么解决?
  19. 工程流体力学笔记1(质点导数的公式与定义)
  20. idog copy from,

热门文章

  1. debian 7 调整控制台分辨率
  2. php 计算工资,php计算税后工资的方法
  3. 计算机夏令营英语面试,保研经验 | 夏令营面试那些事儿(内含视频)
  4. nios自学笔记四:将sof和elf合并为JIC文件
  5. NDT 公式推导及源码解析(1)
  6. 计算机游戏玩法,123木头人游戏规则与玩法
  7. 【防火墙流控配置 基于主机的带宽控制】
  8. CA认证过程及https实现方法
  9. SaaS到底是什么?如何做?
  10. 度歌虎山寨搜索引擎创始人竟是“90后”