C++编译与链接(2)-浅谈内部链接与外部链接
发现每次写技术博客时,都会在文章开头处花费一番功夫
...从前,有一个程序员....他的名字叫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 ;
}
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 ;
}
那么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)的定义,就会出现无法解析的外部符号的错误
宏是内部链接还是外部链接
答:都不是,宏在预处理环节时就被替换掉了,而内部链接与外部链接是针对编译环节与链接环节而言的
C++编译与链接(2)-浅谈内部链接与外部链接的更多相关文章
- 防止apk反编译的技术分析浅谈--内存修改器篇
声明: 1.本帖转载自http://jingyan.baidu.com/article/a24b33cd509eb719fe002b94.html,仅供自用,勿喷 Apk反编译修改器有很多.拿其中的比 ...
- 咱妈说别乱点链接之浅谈CSRF攻击
平时经常听到人们说别乱点链接,小心有病毒.还有长辈们转发的“天呐~XXX的阴谋,全是病毒”.“XXX惊天大病毒,点了苹果手机就要爆炸!”.“现在转发热门连接会乱扣费!千万别点!”. 到底长辈们说的这些 ...
- 转:浅谈CSS在前端优化中一些值得注意的关键点
前端优化工作中要考虑的元素多种多样,而合理地使用CSS脚本可以在很大程度上优化页面的加载性能,以下我们就来浅谈CSS在前端优化中一些值得注意的关键点: 当谈到Web的“高性能”时,很多人想到的是页面加 ...
- 浅谈Ddos攻击攻击与防御
EMail: jianxin#80sec.comSite: http://www.80sec.comDate: 2011-2-10From: http://www.80sec.com/ [ 目录 ]一 ...
- [原创]浅谈如何使用gcc开发NT核心驱动程序
原文链接:[原创]浅谈如何使用gcc开发NT核心驱动程序 一谈到在 Win NT 下开发核心驱动程序,可能不少人首先都会想到微软“正统”的VC来.诚然,用VC 配合 WINDDK 的确工作的不错,但或 ...
- 转载-浅谈Ddos攻击攻击与防御
EMail: jianxin#80sec.comSite: http://www.80sec.comDate: 2011-2-10From: http://www.80sec.com/ [ 目录 ]一 ...
- MVC5-11 浅谈拦截器
Filter拦截器 Aop是MVC的主要设计方式之一,而微软也希望我们在使用MVC的时候更好的使用拦截器来进行切面编程.拦截器则是Mvc中的一大亮点与重点 AOP(面向切面)编程已经广泛应用在各个项目 ...
- 浅谈ImageList
ImageList组件用了很久,但是一直不太清楚它的实现原理,今天专门特意花了时间倒腾了下,终于弄明白了!于是在这里和大家分享下! 在设计页面中打卡工具箱-组件 找到ImageList组件,将它直接拖 ...
- 浅谈数据库并发控制 - 锁和 MVCC
在学习几年编程之后,你会发现所有的问题都没有简单.快捷的解决方案,很多问题都需要权衡和妥协,而本文介绍的就是数据库在并发性能和可串行化之间做的权衡和妥协 - 并发控制机制. 如果数据库中的所有事务都是 ...
随机推荐
- Scala(三):类
类:Class 1.简单类和无参方法 2.带getter和setter属性 3.只带getter属性 4.对象私有字段 5.Bean属性 6.辅助构造器 7.主构造器 8.嵌套类 1.简单类和无参方法 ...
- ExcelVBA实现一键生成word文字报告及批量操作[原创]
在很多工作中,经常需要写一些类似的报告,使用同一个模板,只是里面的数据不同,人工操作工程量大且容易出错,如果能用程序直接实现可以省去不少麻烦. 本文使用ExcelVBA实现,主要思路是使用word邮件 ...
- # 2017-2018-1 20155224 《信息安全系系统设计基础》第四周MyOD
2017-2018-1 20155224 <信息安全系系统设计基础>第四周MyOD 在这里跟老师先道歉,当时我的git没有安装好,后面也一直没有装上,所以程序没有git. 要求 参考教材第 ...
- noip2017 TG 游记
嗨小朋友们大家好,还记得我是谁吗?我就是为GG代言的蒟蒻--xzz 今天呐我特别的要向HN的dalao们ZJ的巨佬们还有全国的神犇们问声好 为什么呢因为我们在2017年11月份来到了吔屎的长沙理工大学 ...
- SpringBoot日记——Web开发篇
准备开始实战啦!~~~~ 我们先来看,SpringBoot的web是如何做web开发的呢?通常的步骤如下: 1.创建springboot应用,指定模块: 2.配置部分参数配置: 3.编写业务代码: 为 ...
- stl源码分析之hash table
本文主要分析g++ stl中哈希表的实现方法.stl中,除了以红黑树为底层存储结构的map和set,还有用哈希表实现的hash_map和hash_set.map和set的查询时间是对数级的,而hash ...
- .Net 如何访问主流的各大数据库
做过开发的都知道,.NET基本可以理解是和MSSQL,windows服务器属于一个好的搭档,正如PHP和MYSQL,LIUNX等也可以理解是一个完美搭配:但是在实际的开发中并不完全是这样的,如果你是学 ...
- hover时显示可跟随鼠标移动的浮动框,运用函数节流与去抖进行优化
在很多笔试面试题中总能看到js函数去抖和函数节流,看过很多关于这两者的讨论,最近终于在一个需求中使用了函数去抖(debounce)和函数节流(throttle). 需要完成的效果是,鼠标在表格的单元格 ...
- Redis的C语言客户端(hiredis)的安装和使用
关键词:hiredis, cRedis, redis clients, redis客户端, C客户端, 华为云分布式缓存服务 hiredis是一个非常全面的C语言版redis接口库,支持所有命令.管道 ...
- OSSEC 架构
OSSEC由很多部分组成,它有一个集中的管理端,用于监控.并接收来自代理.syslog.数据库或无代理设备的日志. 管理端(服务器) 管理端属于OSSEC部署中的中心部分.它存储了文件完整性检测数据库 ...