三、C++ 链接器 linker

2023-05-16

cilinking:从C++源码到可执行二进制的过程。compile文件之后进行链接,找到每个符号、函数的位置,并将其链接在一起

每个文件被编译成一个独立的.obj文件作为translation unit,这些文件无法自主沟通。写一个程序使用到了多个文件时,使用linker将这些文件链接到一个程序。(<——链接器的工作)。

即使没有外部文件里的函数(仅有一个文件),应用程序为了直到入口点在哪(main函数在哪,程序运行时从main函数开始)

编译有两个阶段:编译与链接

区分方法:ctrl+F7 或 build(编译)文件,仅有编译发生,未发生链接

build整个项目之后,或F5运行时 先编译后链接

编译器complier处理语法错误 C开头, 表示编译阶段发生的错误

链接器linker处理链接错误 LNK开头, 表示链接阶段发生的错误

令Source Files文件夹下仅有Math.cpp文件,

Math.cpp

#include <iostream>

void Log(const char* message) 
{
	std::cout << message << std::endl;	
}

int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;
}

 此时build整个项目会报错LINK:fatal error LNK1561:entry point must be defined

        原因:缺少主函数

 .exe文件必要要有一个切入点

该切入点不一定非得是main函数,但是必须要有切入点

在Math.cpp函数中添加main函数,编译整个项目成功(linker)生成.exe文件,此时Math.cpp

#include <iostream>

void Log(const char* message) 
{
	std::cout << message << std::endl;	
}

int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;
}

int main()
{

}

 再次修改Math.cpp文件

#include <iostream>

void Log(const char* message) 
{
	std::cout << message << std::endl;	
}

int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;
}

int main()
{
	std::cout << Multiply(5,8) << std::endl;
	std::cin.get();//该语句确保控制台不会被瞬间关闭
}

输出

假设这些程序存在于多个文件中:(将重新添加Log函数.cpp文至Source Files),将Math.cpp文件中的Log函数移至Log.cpp文件在,重新编译Math.cpp会得到C开头的error,没有找到Log,原因是Math.cpp不知道Log函数的存在。Math.cpp文件中并未添加Log函数的声明

将Log函数的首行添加至Math.cpp中进行声明,再次编译显示成功

此时build整个项目,会报compile error

 原因:Log函数中并未include <iostream>

Log函数中include <iostream>后,项目compile(linker)成功

Math.cpp

#include <iostream>

void Log(const char* message);

int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;
}

int main()
{
	std::cout << Multiply(5,8) << std::endl;
	std::cin.get();//该语句确保控制台不会被瞬间关闭
}

Log.cpp

#include <iostream> 

void Log(const char* message) 
{
	std::cout << message << std::endl;
}

常见的linking error(链接错误)

①uresolved external symbol(未解决的外部符号)

原因:链接器找不到需要的东西

例如,此时将Log文件中"void Log(const char* message) "Log进行改变,Math.cpp文件中仍然有Log函数的声明,文件仍然会被compile编译(因为没有linker,文件只检查以确保能够正确编译,相信某处有Log函数,但是找到这个函数是链接阶段的工作),此时compile整个项目(linker),会得到linking错误,uresolved external symbol(未解决的外部符号),那个符号缺失了(此处为Log),在何处引用该函数

若将Math.cpp文件中的Log函数注释掉进行编译,linker不会报错,因为linking过程中,并没有使用Log函数,所以原本的Log.cpp如和改变并不影响,因为Math.cpp根本没有使用Log函数,它被注释掉了

若将Math.cpp文件main函数中的Multiply与Log函数同时注释掉(仅需要注释掉含Multiply的语句,因为Log是被Multiply调用的),build项目,可能会得到一个linking error,可能的原因是:虽然文件中没有使用Multiply函数,技术上来讲可能在另一个文件中使用它,因此linker确实需要linking该函数。若有办法告诉编译器该函数——Multiply——仅仅会在此文件中使用,就可以去掉这种linking error(Multiply从未被调用,所以也永远不会调用Log),实现方法:Multiply前加上static,意味着这个Multiply函数只为该单元声明(也就是该例中的Math.cpp文件),此时linking将不会报错。

Math.cpp

#include <iostream>

void Log(const char* message);

static int Multiply(int a, int b)//linking报错时需要添加static
{
	Log("Multiply");
	return a * b;
}

int main()
{
	//std::cout << Multiply(5,8) << std::endl;//Multiply被注释掉
	std::cin.get();
}

一切回复正常后,再将Log函数的返回值类型发生改变。Log.cpp:

#include <iostream> 

int Log(const char* message) //由void改为int
	std::cout << message << std::endl;
    return 0;//此句不加会产生C error
}

再次build项目,linking error:

 改变Log函数中的参数,Log.cpp:

#include <iostream> 

void Log(const char* message, int level) 
{
	std::cout << message << std::endl;
}

 build项目将会再次报错,因为linker链接器要寻找的Log函数并没有另一个参数,仅有一个参数const char *,如果链接器找不到一摸一样的,就会产生linking error

 ②有重复符号(函数或变量有相同的名字和相同的签名)时,两个相同名称的函数具有相同的返回值和相同的参数。此时linker不明确那个link到那个。

Log.cpp函数中重复再写void Log内容后进行编译,编译器会报错:error C2084: 函数“void Log(const char *)”已有主体

即,函数中有重复符号时,编译器会报错,因为都在一个文件中,而不需要链接发生就可以判断。若将重复内容移至其他文件(例Math.cpp),此时编译文件不会出错,但是build项目将会报错,linking error:Log已被定义。链接器不知道该链接至那个函数。

发生可能:

文件复原可对项目成功build之后,新建header file,Log.h

Log.h中进行Log函数的定义,而Log.cpp中仅进行声明

 Math.cpp中的Multiply函数中调用log,Log.cpp的InitLog函数中也调用

//Log.cpp
#include <iostream> //include后,error消失
#include "Log.h"

void InitLog()
{
	Log("Initialized Log");
}

//Log.h
#pragma once

void Log(const char* message) 
{
	std::cout << message << std::endl;
}

//Math.cpp
#include <iostream>
#include "Log.h"

static int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;
}

int main()
{
	std::cout << Multiply(5,8) << std::endl;
	std::cin.get();
}

此时build整个项目,会报错,linking error

 此时三个文件中确实只有一个Log函数定义,报错原因:include语句,Math.cpp与Log.cpp中均进行了#include "Log.h",所以产生了两个Log函数的定义。

        解决方法:①将log函数标记为static. 这时该Log函数链接时只发生在内部,即Log函数被include进Math.cpp与Log.cpp时,只会对该文件内部有效。

static void Log(const char* message)

此时build项目成功

                        ②将Log函数标记为inline. inline的意思是将函数本身拿过来,而不是调用

inline void Log(const char* message) 

此时build项目成功

                        ③将定义移动至一个翻译单元中(报错原因是这个Log函数被包含在两个翻译单元中),可以把Log函数的定义移动至第三个翻译单元,或者把Log函数定义放到现有的一个翻译单元中。(将其放入Log.cpp,Log.h函数中仅留声明)

//Log.cpp
#include <iostream> //include后,error消失
#include "Log.h"

void InitLog()
{
	Log("Initialized Log");
}

void Log(const char* message)
{
	std::cout << message << std::endl;
}

//Log.h
#pragma once

void Log(const char* message); 


//Math.cpp
#include <iostream>
#include "Log.h"

static int Multiply(int a, int b)
{
	Log("Multiply");
	return a * b;

头文件中不放函数的定义,仅包含声明就可以。只声明,不定义。

07

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

三、C++ 链接器 linker 的相关文章

  • main.cpp:(.text+0x5f): 未定义的引用

    我尝试从 SDL 指南中编写一些练习 我这样编译 g o main main cpp I usr local include SDL2 L usr local lib lSDL2 我得到这个 tmp cci2rYNF o In functi
  • 如何在程序中包含数据对象文件(图像等)并访问符号?

    我使用以下命令将几个资源文件转换为 obj 文件objcopy我将它们与我的程序源代码链接起来 我可以使用以下代码很好地访问程序中目标文件内的符号 但只能使用 GCC G Cygwin extern uint8 t data asm bin
  • Mono mkbundle 工具无法创建二进制文件,并抱怨输出文件不可用

    根据来自的建议这个线程 https stackoverflow com questions 551554 can you compile c without using the net framework在运行没有 NET 的 C 应用程序
  • 如何消除警告LNK4221?

    我正在使用 c windows 表单 Visual Studio 2010 开发一个项目 我们有 4 个项目 1 个包含 GUI Windows 窗体 托管代码 的项目 这是 exe 项目 其他3个项目 非托管代码 并且都是静态库 在这 4
  • 如何调试 STL/C++ 的 GCC/LD 链接过程

    我正在用 C 开发裸机 cortex M3 以获取乐趣和利润 我使用 STL 库 因为我需要一些容器 我认为通过简单地提供我的分配器 它不会向最终的二进制文件添加太多代码 因为您只得到您使用的内容 实际上我根本没想到与 STL 有任何链接过
  • 为什么使用 $L 指令链接的目标文件的顺序很重要?

    我使用静态链接的 sqlite 数据库 为了编译每个下一个版本 我有时必须对所使用的对象文件列表进行微小的更改 但有时我必须做出的改变让我感到困惑 例如在版本 3 6 10 之前此命令 L Objs is OBJ L Objs mbissp
  • Boost 单元测试链接错误——abi 不匹配?

    我正在尝试使用 boost 构建一个单元测试 但链接器抱怨缺少函数 拿这个骨架代码 define BOOST TEST DYN LINK define BOOST TEST MAIN include
  • gcc 链接器获取未使用对象的列表

    我想识别具有许多库的大型 C 应用程序中未使用的目标文件 随着时间的推移 该项目已经增长了很多 现在我想搜索不再使用的库 以便我可以从依赖项文件中删除它们 gcc 链接器是否可以识别任何未使用的对象 例如 如果我使用 gcc 编译一个应用程
  • Mac OS X:尝试链接(ld)到框架

    我正在阅读 Mark 和 Aaron 所著的 高级 Mac OS X 编程 我无法让一个终端语句正常工作 cc g o useadd F Adder build framework 加法器 useadd m 它位于第 45 页 第 3 章
  • 在 Linux 上使用“libc++”编译时,未定义对“__cxa_thread_atexit@@CXXABI”的引用

    我正在尝试编译我的项目Arch Linux x64 using libc libc abi and 铿锵 3 6 0 项目编译正确 但无法链接并出现以下错误 错误 CMakeFiles main cpp o 对符号 cxa thread a
  • Android NDK 链接问题

    我用 NDK 编译了 Sox 等 所以 我拥有所有 Android 友好的共享库 我制造了一个简单的测试文件 http pastebin com rniwQ7Gz它调用 sox 函数 NDK 构建告诉我 undefined referenc
  • GNU 链接器 - 孤立部分和符号分配

    在阅读了足够多有关 GNU 链接器的文档后 我对结合有关实现自定义链接器文件的两个不同概念感到困惑 第一个概念是孤儿节 https sourceware org binutils docs ld Orphan Sections html O
  • 使用 g++ 从 cpp 文件和静态库创建共享库 [重复]

    这个问题在这里已经有答案了 就像标题所说 我想从三个 cpp 文件和一些静态库创建共享库 基本上我想这样做 g libProject so file1 cpp file2 cpp file3 cpp I usr local include
  • cmake 生成 Xcode 项目 - 发布构建有效,但存档因链接器错误而失败

    使用 Xcode 6 3 1 CMake 3 2 2 我有一个与图书馆链接的项目 该库作为代码包含在 xcode project 中 经过编译 然后与主可执行文件链接 项目是用cmake生成的 CMakeLists txt 的一些摘录 ad
  • Ubuntu 11.10 上的 c 数学链接器问题 [重复]

    这个问题在这里已经有答案了 我从 Ubuntu 升级后出现了一些奇怪的错误 10 11 11 04 i dont know 到 11 10 我正在得到一个undefined reference to sqrt 使用 math h 时并与 l
  • 在 Visual Studio 2012 中安装 OpenCV

    我正在尝试安装 OpenCV 来与 Visual Studio 一起使用 我使用的是2012Pro版本 但我认为它应该与vs10相同 我正在关注这个教程 http docs opencv org doc tutorials introduc
  • 无法链接 Boost 正则表达式

    我目前正在尝试编译一个KIT 的收缩层次实现 http algo2 iti kit edu english routeplanning php这需要 Boost Regex 提供的 Makefile 已经确保 并且我还手动仔细检查了这一点
  • 链接器问题 - 未定义的引用

    我的编译器有问题 告诉我有一个 未定义的引用 我想在库中使用的函数 让我分享一些有关该问题的信息 我正在用 gcc 交叉编译 C 语言 我正在调用一个库函数 该函数通过包含的标头访问 其中包含另一个标头 其中包含原型 我已经使用 I 包含了
  • netdb.h 未正确链接

    我正在尝试编译这个程序 如引用的Beej 的网络编程指南第 19 页 include
  • Visual Studio 2010 库链接错误

    当我尝试在项目中使用 DCMTK MT 库时 出现以下链接错误 我确信这些错误与VS2010的ws2 32 lib wsock32 lib netapi32 lib有关 但我已经将这三个库添加到项目属性中 您可以看到库已被搜索 如下所示 这

随机推荐

  • spi通信

    特点 常规四线通信方式 一根片选线 xff0c 一根时钟线 xff0c 两根数据线 xff1a 一根是主进从出线 xff0c 一根主出从入线 同步 xff0c 全双工 xff0c 通信方式 和谁通信就将谁的片选线拉低 xff0c 每增加一个
  • FreeRtos任务通信之消息队列

    入队阻塞 xff1a 往队列写数据时 xff0c 队列满时的阻塞 当阻塞结束时 xff0c 优先级最高的任务优先进入就绪态 xff0c 同 优先级时 xff0c 阻塞时间最长的进入就绪态 出队阻塞 xff1a 读队列时 xff0c 队列为空
  • STM32F103C8T6 ADC功能

    12位逐次逼近型ADC 1us转换时间 输入电压0 3 3v和转化范围0 2 12 1 xff08 0 4095 xff09 成线性关系 2个ADC资源 xff0c ADC1和ADC2 xff0c 10个外部通道 有两种转换单元组 xff1
  • stm32串口实验

    目录 xff08 一 xff09 STM32 串口简介 二 软件设计 xff08 三 xff09 效果 xff1a 1 实现功能 xff1a STM32 通过串口和上位机的对话 xff0c STM32 在收到上位机发过来的字符串后 xff0
  • 基于stm32的GPS解析数据

    目录 1 GPS模块 2 GPS发送的数据格式 3 软件设计 3 1配置好串口 3 2然后写串口中断函数 效果 1 GPS模块 2 GPS发送的数据格式 GPRMC lt 1 gt lt 2 gt lt 3 gt lt 4 gt lt 5
  • 大疆遥控控制M3508电机二(基于HAL库)

    接上一篇文章 xff0c 话不多说直接开始 一 打开我们创建的工程文件 xff0c 先就建立一个文件夹用来存放我们写的子文件 xff08 不建立也行 xff09 xff0c 然后建立pid h xff0c pid c存入我们建立的文件夹中
  • aruco_ros 在相机图像中显示的坐标轴姿态与TF发布的姿态不一致 解决方法

    aruco ros 在相机图像中显示的坐标轴姿态与TF发布的姿态不一致 解决方法 运行环境问题描述解决方案 运行环境 系统版本 xff1a Ubuntu 16 04 ROS版本 xff1a Kinetic 问题描述 在进行手眼标定过程中 x
  • 安装vscode的时候没有勾选快捷方式用vscode打开文件,如何用vscode快速打开文件或者文件夹

    1 需要在注册表里面添加内容可以实现 xff0c 下面有一种简单的方式来完成这个操作 2 需要在电脑桌面新建一个Open File With VS code reg 文件然后用打开文件 xff0c 将以下代码复制 Windows Regis
  • C语言学习笔记->const和define区别

    1 define 是预编译指令 xff0c 而const 是普通变量的定义 define定义的宏是在预处理阶段展开的 xff0c 而const定义的只读变量在编译运行中使用 2 const定义的是变量 xff0c 而define定义的是常量
  • 基于栈、寄存器的优缺点

    基于栈的优点 xff1a 对硬件的依赖不高 xff0c 跨平台性 缺点 xff1a 使用的指令集高基于寄存器的优点 xff1a 相对于栈来说 xff0c 寄存器的性能高 xff0c 使用的指令集少 Java代码执行流程 xff1a java
  • Makefile文件的编写(实例详解)

    1 什么是Makefile xff1f 一个工程中的源文件不计其数 xff0c 其按类型 功能 模块分别放在若干个目录中 xff0c Makefile定义了一系列的规则来指定哪些文件需要先编译 xff0c 哪些文件需要后编译 xff0c 哪
  • 用C语言实现万年历的代码及思路(详细教程)

    万年历程序要求 xff1a 1 当选择1的时候 xff0c 输入年 xff0c 打印输入的这一年12月的日历 2 当选择2的时候 xff0c 输入年 月 xff0c 打印输入这一年这一月的日历 实现效果 xff1a 选择1时 span cl
  • 有符号数和无符号数参与运算时的问题

    陷阱题目 xff1a 下面的代码输出是 xff08 xff09 span class token macro property span class token directive hash span span class token di
  • 【Linux网络编程】基于UDP实现多人聊天室

    文章目录 一 UDP的概念1 1 UDP1 2 UDP特点 二 采用UDP实现多人聊天室原因三 多人聊天室项目功能四 实现多人聊天室项目流程分析4 1 前期准备4 1 1 定义结构体4 1 2 定义链表 4 2 多人聊天室服务器4 2 1
  • 【C++】C向C++的知识过度(上)

    文章目录 一 C与C 43 43 的区别1 1 C是面向过程的1 2 C 43 43 是面向对象的1 3 编译器的区别 二 C与C 43 43 默认代码的不同三 命名空间3 1 关键字 96 namespace 96 去定义自己的名字空间
  • 【C++】四种类型转换 | C++异常处理机制 | C++11新特性之右值引用和移动构造

    文章目录 一 C 43 43 中的四种类型转换1 1 静态类型转换1 1 1 C 43 43 中内置类型的转换1 1 2 C 43 43 中的有继承关系存在的场景下的类型强转 1 2 动态类型转换1 3 常类型转换1 4 解释类型转换 二
  • 【Linux操作系统】进程详解(上)

    文章目录 一 进程的定义二 进程的特征三 进程的组成及其作用四 进程控制块4 1 进程控制块定义4 2 task struct的内容 五 进程与程序的区别六 进程与线程的区别七 进程的种类八 PID8 1 PID定义8 2 特殊PID的进程
  • 【Linux操作系统】进程详解(下)

    文章目录 前言一 父子进程共用光标问题1 1 验证1 2 规避共用光标问题使用多进程拷贝同一个文件 二 进程相关函数2 1 getpid getppid函数2 2 exit exit函数2 3 wait waitpid函数 三 守护进程3
  • git补丁操作(git diff、git format-patch、git apply、git am)

    https blog csdn net qq 42138454 article details 119058431 实际使用补丁用的 xff1a patch p1 lt device nuwa diff 一 打补丁的两种方法 Git 提供了
  • 三、C++ 链接器 linker

    cilinking 从C 43 43 源码到可执行二进制的过程 compile文件之后进行链接 xff0c 找到每个符号 函数的位置 xff0c 并将其链接在一起 每个文件被编译成一个独立的 obj文件作为translation unit