内部链接(internallinkage)/外部链接 (externallinkage)是和编译单元(translation unit)相关的一个术语,其主要影响函数或者对象的作用域及存储方式—是全局只存储一个,还是全局有许多量的副本。

具有externallinkage的变量可以被其它源文件使用,整个程序内有效,并且全局只有一个。

具有internallinkage的变量只能被本translation unit所引用,如果有多个translation unit,则会有多个副本—每个cpp文件中都会有一个。

1.       编译单元(translation unit)

一个.cpp文件或者.c文件才称为一个translation unit,头文件(.h)不能称为一个translation unit,因为其最终会被加(plus)入到include它们的.cpp/.c文件中去,编译器编译的是.cpp/.c文件,而不是.h文件。

2.       缺省的链接方式

在缺省情况下,常量(const)具有static属性(internal linkage),而非常量(non-const)具有extern属性(external linkage)。

常量(const)因为具有static的internal linkage属性,其会导致每个使用它的cpp文件中都会包含一个const副本,这个副本是local的,可能会导致程序中有大量的常量副本。

变量具有extern的external linkage属性,如果在头文件中定义变量并且该头文件被多个cpp文件引用,将导致重复定义的编译错误;对于变量正确的做法应该是在cpp文件中定义,然后在使用它的cpp文件中使用extern来声明它,即不要将变量直接暴露在.h文件中,如果一定要在头文件中暴露变量,那么应使用extern来修饰它。

非extern实例声明即是定义

对于实例,除了类/结构的静态成员外,其声明处就是定义处,即声明等同于定义,其不存在函数有函数声明和函数定义两个部分。测试证明:

在一个类的.h文件中   : int g_testDef;

在该类相应的.cpp文件中: int g_testDef=0;

编译.cpp文件,则报告下列错误:

error C2086: 'int g_testDef' : redefinition

即.h中已经定义了一个叫做g_testDef的常量,在.cpp文件中又定义了它,即cpp文件中对g_testDef的使用不是初始化它,而是重新定义一个叫做g_testDef的变量并立刻初始化之为0。在.h文件中是只定义了g_testDef而没有显式地初始化它—它会被编译器初始化为随机值或者特定的值(该特定值往往在debug版本中被认定为未初始化的内存)。编译器并不强制要求用户在定义处就初始化变量,因为该变量可以由用户在其它处重新赋值。

如果使用const方式:

在一个类的.h文件中   : int const g_testDef;

在该类相应的.cpp文件中: int const g_testDef=0;

编译.cpp文件,则报告下列错误:

error C2734: 'g_testDef' : const object must be initializedif not extern

error C2086: 'const int g_testDef' : redefinition

即首先一个错误:非extern常量必须定义处初始化(在.h文件中)。

其次的重定义错误和非const一样。

extern实例的定义与非定义

对于extern实例,如果没有extern修饰,则声明处就是定义处;而对于用extern修饰的extern实例,可以有多处;但是赋值处只能有一处,并且只有赋值处才被视为定义处

int g_testDef;                        //definition,①

int g_testDef=2;                    //definition, ②

extern int g_testDef=2;          //definition,③

extern int g_testDef;              //non-definition,④

在上面①②③④混用的情况下,④可以在一个文件或者多个文件中多次出现,①②③等只允许其中一种形式出现一次并且必须出现一次,如果一次都没有出现,则链接时会报告"errorLNK2001: unresolved external symbol",如果出现超过一次,则链接时会报告"errorLNK2005: "int g_testDef" (?g_testDef@@3HA) already defined ininit_1.obj"。

即对于extern量,定义必须存在并且只允许定义一次,而非定义则可以多次。

对于const量,由于没有第①种形式(常量定义处必须初始化),其它三种形式的使用和non-const一样。

例如:

//1.cpp : 注意是.cpp文件

const int g_internalLinkage=2;

//2.cpp : 注意是.cpp文件

externconst int g_internalLinkage;

voidtestInternalLinkage()

{

printf("%d",g_internalLinkage);

}

上面代码可以顺利通过编译,但是在link时则报告下列错误:

error C2065: 'g_internalLinkage' : undeclared identifier

即在1.cpp中,g_internalLinkage因为是const的,缺省不是externallinkage的,其不会被暴露给全局。

在2.cpp中,虽然g_internalLinkage是external linkage的,但是它自己没有被初始化,所以被认定为引用全局extern的标签,在链接时,linker向全局寻找g_internalLinkage,但没有发现extern定义的g_internalLinkage,所以链接失败。

将定义的g_internalLinkage修饰为extern即可。

//1.cpp : 注意是.cpp文件

externconst intg_internalLinkage=2;

//2.cpp : 注意是.cpp文件

externconst int g_internalLinkage;

voidtestInternalLinkage()

{

printf("%d",g_internalLinkage);

}

编译链接OK。

在头文件中定义变量将导致该头文件只能被一个cpp文件include

如果在头文件中定义了一个变量(无extern修饰词),然后在多个cpp文件引用这个.h文件,将导致每个include它的cpp文件中都有该变量(include就相当于把头文件中所有内容完全复制到cpp中一样,cpp将拥有.h中的所有内容),编译后生成一个编译单元(translation unit),由于变量缺省是external linkage的,则链接时该变量会扩散到全局以供其它编译单元也可以使用/修改它。这样问题就来了,如果多个编译单元都include了同一个包含有变量的头文件,那么这多个编译单元里都有这个全局变量。如果不链接,那么没有问题。一旦链接,则不同编译单元里的该全局变量就会导致重定义冲突。事实上是变量重名冲突,地址并不冲突的,因为各个全局变量都是都是在各自的translationunit里的。

在一个头文件(head.h)中定义一个变量:

int g_count=0;

然后有多个.cpp文件include这个头文件(init_1.cpp,init_2.cpp,init_3.cpp),所以的源文件都能顺利通过编译,但当链接时,则报告下列错误:

error LNK2005: "int g_count"(?g_count@@3HA) already defined in init_1.obj

即其它cpp文件中也有g_count这个定义。奇怪?难道不是三个cpp文件共享head.h中定义的变量g_count么?事实上似乎是每个包含head.h的cpp文件中都有一个g_count变量的定义。事实上的确如此,下面可以进一步通过常量验证。

在头文件中定义常量将导致该常量在程序中有大量副本

在头文件中声明一个const变量g_testH_CPP,并使用初始化函数getH_CPP来初始化它。

在第一个.cpp文件(init_1.cpp)中使用该const量:

在第二个.cpp文件(init_2.cpp)中使用该const量:

运行测试程序:

输出:

从输出可以看到,在两个.cpp文件中引入同一个.h文件中定义的常量,该常量的初始化函数initH_CPP()函数被调用了4次,并且在这两个cpp中使用的常量的地址不相同,这证明了这两个名字相同的常量,实际并不是同一个。

初始化处才是extern量的定义处

无论是常量还是变量,如果是extern的,只有初始化处才被认为是定义处,其它extern被认为是引用的标签,标识链接时需要linker全局寻找该量的定义。

例子:

externconst intg_internalLinkage=2;  //定义一个extern的常量,只允许一处

externconstintg_internalLinkage;                  //声明引用一个extern常量,可以允许多处

如果有多个同名的extern量在定义时都初始化了,编译都是成功,但链接时会失败,报告重复定义错误:

error LNK2005: "int g_testDef" (?g_testDef@@3HA)already defined in init_2.obj

fatal error LNK1169: one or more multiply defined symbolsfound

即linker发现了全局scope内有多个同名量的定义(初始化赋值),这是必然的突然。

3.       明确地指定链接方式

除了缺省情况外,设计者还可以明确地指定一个量的链接方式,这个指定对变量和变量都有效。

Ø  用extern声明一个量,则其具有外部链接作用域,该量既可以是常量也可以是变量。

Ø  用static声明/定义一个变量,则其具有内部链接作用域,该量应该是变量,因为常量使用了const,而const缺省就是static的,可以省略static修饰词。(也可以是常量,但是static 和const修饰词同时使用的时候,static是多余的)

C++提倡static只使用在类的静态成员上,对于变量和函数,如果需要其具有internallinkage,推荐使用anonymous namespaces来实现。

附:作用域(scope)和生存期(lifetime)不是同一概念。作用域指某tanslation unit中定义的变量是否对其它tanslation unit可见(即其它tanslationunit中是否可以使用这个变量/函数);生存期指一个变量的存活时间(从构造开始到析构结束)。

Auto和register属性的量有local生存期,而static及extern属性的量具有global生存期。

内部链接(internal linkage)和外部链接(external linkage)相关推荐

  1. 已解决: 什么是内部链接,什么是外部链接?

    这里并没不是讨论大学课程中所学的<编译原理>,只是写一些我自己对C++编译器及链接器的工作原理的理解和看法吧,以我的水平,还达不到讲解编译原理(这个很复杂,大学时几乎没学明白). 要明白的 ...

  2. vue a链接跳转打开外部链接

    背景: vue单页应用项目直接使用a标签跳转到外部链接报错 问题分析: a标记触发的跳转默认被router处理,加上了前缀,见如下代码: <--a标签--><ahref=" ...

  3. php 页面生成外部链接,php 获取网页外部链接正则表达式

     代码如下 复制代码 preg_match_all("//i",$webContent,$link); $urls =array(); foreach($link[0] as $v ...

  4. C++编译单元 内部链接 外部链接

    文章目录 编译单元 内部链接 外部链接简单解释 代码解释 外部链接 内部链接 C++ 中的内部链接 和外部链接 类型 编译单元 内部链接 外部链接简单解释 这是一个最简单最表面的解释,深入的解释应该要 ...

  5. 网站应该更注重内部链接还是外部链接?

    在搜索引擎上,去获取流量的最基本单位就是网页.一个网页的外部链接因素,对这个网页的排名影响很大.这个网页的外部链接,既有同一个网站的其他页面给的站内链接,也有其他网站上的网页给的站外链接.下面文章里的 ...

  6. 各类链接(外部链接、内部链接……)的使用方法合集

    上篇讲了"网站SEO关键词布局操作大全",这篇讲链接,网站是通过一个一个链接搭建到一块的,所有网站之间以及网站和用户之间也都是通过链接进行联系起来的,同样也是搜索引擎爬取和识别的线 ...

  7. 超链接标签(外部链接、内部链接、空链接、下载链接、网页元素链接、锚点链接)、注释

    超链接标签 在HTML中,<a>标签用于定义超链接,作用是从一个页面链接到另一个页面 1.链接的语法格式 <a href="跳转目标" target=" ...

  8. C语言中变量存储类别——自动变量,寄存器变量,静态外部链接;

    c提供了多种不同模型或存储类别在内存中存储数据. 作用域: 作用域描述程序中可访问标识符的区域. 作用域描述了程序中可以访问一个标识符的一个或多个区域.即变量的可见性. 一个变量的作用域可以是代码块作 ...

  9. 外部链接锚文字包含关键词

    一.排名因素调查 大正面排名因素 1 用户粘度 2 外部链接锚文字包含关键词 3 链接来源多样性(链接来自于很多不同域名) 4 外部链接流行度(外部链接的数量质量) 5 标题标签中任何地方使用关键词 ...

最新文章

  1. 路由器的×××流量过滤
  2. 在VC中如何使用OCX控件 【来源:http://blog.csdn.net/wulang1114/article/details/4806089】
  3. python连接access 参数太少_带参数的PypyODBC:[ODBC Microsoft Access Driver]参数太少。预期4...
  4. springboot中端点监管 endpoint actuator
  5. 转载:介绍AD另外一种奇葩的多通道复用的方法
  6. 项目分享:通过使用SSH框架的公司-学员关系管理系统(CRM)
  7. 2015蓝桥杯省赛---java---C---3(无穷分数)
  8. 学生信息系统求助_一个学生信息录入和查询的系统
  9. 烟台市与拼多多达成战略合作 启动烟台优品线上云购节活动
  10. 面试官问你是true还是false你可以最后反问他这个
  11. 数据结构实验 6.宗教信仰
  12. linux BufferedImage.createGraphics()卡住不动
  13. linux系统开远程桌面,Linux 系统开启远程桌面的方法
  14. 《21天学通Java(第6版)》—— 导读
  15. 电脑更新后,设备管理器未知USB设备,并且蓝牙无法使用
  16. php flv 转成 mp4,PHP 利用 ffmpeg 把flv转成MP4格式(Linux)
  17. 第五章 语法分析——自上而下分析
  18. 扫雷计算机教案,三年级上信息技术教案-扫雷大赛“鼠标的使用”清华版
  19. 如何查看yandex文字搜索广告的搜索词?
  20. 服装ERP应用(20) ERP在分销型服装企业的物流管理

热门文章

  1. python--计算圆的周长与面积
  2. Coco Chanel(解读香奈儿)
  3. APM 学习8 --- RC 输入输出
  4. 读《少有人走过的路》的一点感想
  5. Go语言全栈开发:结构体
  6. Python3.6安装turtle模块
  7. 学习笔记——Gitee中Push rejected
  8. 如何禁用Mac防火墙激活软件
  9. 设计师的最爱:免费素材网站
  10. 防沉迷系统的设计与实现