从编译链接到cmake
.c(.cpp)文件到可执行文件
对于一份简单的.c/.cpp为后缀的源文件,他所使用的语言是人类可以阅读并看懂的,但是对于计算机来说,其可理解并执行的是二进制的机器码。
也就是说,计算机所能运行的是二进制的机器码,而早期为了方便人类阅读,使用一些简单的助记符来代替机器码,比如
MOV,LOOP...等,而为了进一步增加编程的可读性,便诞生了c语言以及后续的一众高级语言,如c++,python等。
#include <stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
} // 人类语言,计算机仅可执行01010101....重复的机器码
因此要想让我们写出的c或者cpp代码变为计算机可以执行的二进制机器码,就需要使用一些程序来通过一些手段将源代码编译成可执行的二进制机器码。
一般来说,编译器的工作流程如下:
- 预处理: 源代码中的宏进行一切的宏展开,宏替换,以及条件编译等操作,此操作后文件中无任何宏定义。(.i文件)
- 编译:将预处理后的文件通过编译器的词法分析,语法分析,语义分析,优化,代码生成等操作,生成对应的汇编语言文件(.s文件)
- 汇编:将汇编语言文件通过汇编器的汇编操作,生成对应的机器码文件(.o文件)
- 链接:将多个.o文件进行链接,生成最终的可执行文件(.exe文件/linux下无后缀)
因此,编译器所做的工作就是通过自己的一套规则将源代码转换为机器码。规则的制定一般是有对应的语言标准来规定,而编译器可以通过不同的实现来实现该标准即可。编译器更像是一个翻译软件,而它的工作就是根据人类的翻译需求来将一种语言翻译成另一种语言。常见的编译器有gcc(开源),clang(jetbrain系列),msvc(微软)等,不同的编译器支持的最新标准也不同。
至于编译器是怎么来的,可以上网搜一搜。
本次培训使用的编译器是gcc,可以使用
gcc --version命令查看当前gcc版本。
若没有安装gcc,可以使用sudo apt-get install build-essential命令安装gcc。
下面我们以gcc为例,来看一下如何将.c/.cpp文件一步一步编译成可执行文件。
#include <stdio.h>
#define first 1
int main()
{
printf("Hello, world!\n");
int num = first;
printf("%d", num);
return 0;
} // 假设hello.c
预处理
在终端中输入gcc -E hello.c -o hello.i指令,-E选项表示只进行预处理,-o选项指定输出文件名为hello.i。
然后打开hello.i文件,可以看到经过预处理后的源代码如下:
# 0 "hello.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
.....//此处省略一堆东西
int main()
{
printf("Hello, world!\n");
int num = 1;
printf("%d", num);
return 0;
}
如果回到.c文件中,我们查看stdio这个头文件,可以发现其中的内容和hello.i文件前面的内容是一样的,无非是头文件的宏直接变成了头文件所在的路径。而且我们定义的宏first也被替换成了1。
编译
在终端中输入gcc -S hello.i指令,-S选项表示将文件进行编译,输出文件为翻译为汇编的.s文件。
然后打开hello.s文件,可以看到经过编译后的源代码如下:
.file "hello.c"
.text
.section .rodata
.LC0:
.string "hello world!"
.LC1:
.string "%d"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movl $1, -4(%rbp)
........;省略一堆
可以看出,c语言通过编译器变为了可读性更差的汇编语言,但是还有一些东西是人可以看懂的,比如mov,call等指令。
汇编
在终端中输入gcc -c hello.s 指令,-c选项表示将文件进行汇编,输出文件为二进制文件hello.o。

可以看到确实变成二进制文件了,而且由于没有对应的解码器,无法直接看到其中内容。
链接
二进制文件链接
所谓的链接,就是将多个.o文件进行链接,生成最终的可执行文件。对于链接,针对的是多文件项目的编译,因为一个可执行文件中可能包含多个源文件,而这些源文件可能包含不同的函数,因此需要将这些源文件中的函数进行链接,生成最终的可执行文件。
对于上述的hello.o文件,由于是单文件,因此可以简单的使用
gcc hello.o -o hello生成可执行文件hello然后运行
./hello
Hello, world!
对于多文件来说并非如此,比如我们有两个个文件:
int sub(int a, int b)
{
return a - b;
} // sub.c
#include <stdio.h>
int sub(int a, int b); // 声明sub函数
int main()
{
int a = 10, b = 5;
int result = sub(a, b);
printf("%d", result);
} // main.c
如果直接使用gcc -c main.c生成可执行文件,回报出如下错误:
main.c:(.text+0x25): undefined reference to `sub'
即找不到sub函数的定义,这是显然的,sub函数的实现在sub.c中,main.c中我们只是告诉了编译器有一个函数sub,这个行为在预处理->编译->汇编的过程中不会报出任何错误,但是在链接过程中,编译器无法在参与链接的文件中找到sub的实现,因此报出了链接错误。
因此要想不报错,可以这样:
gcc main.c sub.c -o main,这样编译器会将两个文件编译为二进制文件,并将sub.o参与到链接过程中,生成最终的可执行文件main。要注意,链接的是二进制文件,而不是源文件。
对于使用多个二进制文件参与链接生成可执行文件的方法,其好处就是,将项目拆分为多个模块,当要对项目的某一部分进行修改时,只需要重新编译该模块为新的.o文件即可,而不需要重新编译整个项目。
库文件链接
库文件是指一些预先编译好的二进制文件,可以被多个程序共用。分为动态库(linux下的.so文件/windows下的.dll文件)和静态库(linux下的.a文件/windows下的.lib文件)之分。它们的区别就是静态库是连接时全部链接进可执行文件,而动态库是运行时才链接进可执行文件。这里就不演示使用gcc生成和链接库文件的过程了,可以参考相关资料。
至此,我们就走遍了如何从源代码到可执行文件的整个流程。
cmake
可以看到,对于比较大的项目,使用gcc的命令来进行编译会很繁琐和麻烦,就像由机器码到汇编再到c语言一样,为了简化编译流程,便有了从gcc -> makefile -> CMakeLists这样的变化。
简单介绍makefile
Makefile 是用于管理项目构建过程的工具,广泛用于 C/C++ 等语言的编译。它通过定义规则和指令,自动化编译、链接等步骤,大大简化了开发者的工作。也就是说,makefile通过一些规则来告诉编译器如何编译源代码,如何链接库文件,如何生成可执行文件。通过编写规则即可完成编译,而不需要手动指定每个编译选项。(我也不会makefile,这里简单介绍一下)当编写完makefile后,只需要运行make命令,makefile就会自动按照规则编译生成对应的可执行文件。注意make命令后接的是makefile的路径。
cmake与CMakeLists.txt
对于一些复杂的项目,makefile的规则编写起来会比较麻烦,因此出现了cmake。cmake是一种跨平台的编译工具,可以用来管理项目的构建,通过编写cmake,可以自动生成对应的makefile,然后运行make命令编译生成可执行文件。
cmake的配置文件是CMakeLists.txt,它是cmake的核心配置文件,主要用于定义项目的源文件、头文件、库文件等信息,以及编译选项等。
最简单的CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.6)
# 指定项目所需要的cmake最小版本
project(hello)
# 指定项目名称
add_executable(hello hello.cpp)
# 添加可执行文件hello,并指定源文件为hello.cpp
结束后,在终端中运行cmake.命令,cmake会自动生成对应的makefile,然后运行make命令编译生成可执行文件。但是在生成的时候会出现很多配置文件,因此习惯上建立一个名字为build的文件夹,然后在该文件夹下运行cmake..命令,生成的makefile就在build文件夹中,然后运行make命令编译生成可执行文件。
后面关于cmake的使用,转如下链接
安装opencv库并使用
所谓的库,即为我们提供了头文件以及一些预编译好的二进制文件,我们可以通过安装库文件来使用一些功能。
编译安装opencv
这里我们采用下载源码编译安装的方式来安装opencv。所谓的安装,其实就是将别人写好的代码编译为库,然后将头文件和编译好的库文件拷贝到系统的指定目录,同时还附带着一些说明文件,方便编译时找到头文件和库文件。
源码下载链接[https://github.com/opencv/opencv/releases]
安装教程参考:
[https://blog.csdn.net/weixin_44796670/article/details/115900538]
使用opencv
参考cmake的简单使用后半部分
从编译链接到cmake的更多相关文章
- 编译驱动链接到了Kernel32库问题
最近开始学习驱动编程,根据网上的配置方法配置了驱动开发环境,用了一个简单的例子测试发现驱动居然链接到了kerner32库里面去了如图 : 显然是把Kernel.lib添加到了附加依赖库 如图 : 去掉 ...
- SQL SERVER 2012/2014 链接到 SQL SERVER 2000的各种坑
本文总结一下SQL SERVER 2012/2014链接到SQL SERVER 2000的各种坑,都是在实际应用中遇到的疑难杂症.可能会有人说怎么还在用SQL SERVER 2000,为什么不升级呢? ...
- 使用Powershell链接到Office 365
今天主要讲使用Powershell管理Office 365 可以分为office365用户管理,Exchange Online的管理等 1. 使用Powershell 链接到office 365 用户 ...
- java程序链接到sql server数据库
package jianhua; import java.sql.*; public class ConDatabase { public static void main(String[] args ...
- MVC Controller 链接到 API Controller 以及反向链接
MVC Controller 链接到 API Controller 以及反向链接 问题 想创建一个从 ASP.NET MVC controller 到 ASP.NET Web API controll ...
- Oracle中使用透明网关链接到Sqlserver[Z]
Oracle中使用透明网关链接到Sqlserver 在最近项目中需要从Oracle中访问SQL Server数据库, 自然想到了透明网关. 因为Oracle数据库是Linux上的, 而Linux上的O ...
- SharePoint 2010 中创建超链接到Pop-Up对话框
SharePoint 2010 中创建超链接到Pop-Up对话框 SharePoint 2010 推出了新式的带有阴影的弹出对话框,你感觉怎么样?我感觉倒是挺酷的.这样少打开了一个页面 ...
- 海思编译链编译出现__aeabi_unwind_cpp_pr1重定义怎么回事
1.用arm-hisiv100nptl-linux-gcc编译代码,结果发现报错,__aeabi_unwind_cpp_pr1重定义,在librt.a先定义,使用的海思芯片是hi3520d. 2.本来 ...
- Oracle中使用透明网关链接到Sqlserver(转)
测试环境介绍 1.ORACLEServer Database version:10.2.0 IP:192.168.1.5 ORACLE_HOME:D:\oracle\product\10.2.0\ ...
- Windows Azure Web Site (19) Azure Web App链接到VSTS
<Windows Azure Platform 系列文章目录> 之前遇到一个问题,客户在海外使用 我参考了一下国内Azure China的文档:https://school.azure.c ...
随机推荐
- Java常用类——包装类 小白版个人推荐
包装类及自动装箱/拆箱 包装类是将Java中的八种基本数据类型封装成的类,所有数据类型都能很方便地与对应的包装类相互转换,以解决应用中要求使用数据类型,而不能使用基本数据类型的情况. int a = ...
- MAC地址格式详解
以太网编址 在数据链路层,数据帧通常依赖于MAC地址来进行数据交换,它如同公网IP地址一样要求具有全球唯一性,这样才可以识别每一台主机.那么MAC地址如何做到这点?它的格式又是什么? MAC地址,英文 ...
- 老母鸡安装mqtt
大佬写好了, 按部就班操作一下就行了,在hass中添加mqtt可进行测试 玩客云搭建MQTT服务器 - 崔安兵 - 博客园 (cnblogs.com)
- MarginNote 4 内存泄露?
在床上用电脑的时候突然发现电脑风扇呼呼响,一摸很烫,以为是被子把出风口堵住了,于是调整角度继续用.结果一段时间之后风扇还是狂转不停,然后收到了这样的提示.不看不知道一看吓一跳,MarginNote 4 ...
- Java多线程并发之同步容器和并发容器-第一篇
Java多线程并发之同步容器和并发容器-第一篇 概述 本文主要讲解在Java多线程并发开发中,集合中有哪些支持并发的的.什么是同步容器(集合),什么是并发容器(集合)?并发容器分类有哪些?每个分类都有 ...
- MyBatis日志工厂
目录 日志工厂 标准日志实现 Log4j 使用步骤: 日志工厂 我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率? 如果一个 数据库相关的操作出现了问题,我们可 ...
- JS的可选链操作符(?.)与双问号(??),你用到了吗?
可选链操作符(?.) 以前一般这样使用: let nestedProp = obj.first && obj.first.second; 或者这样: let temp = obj.fi ...
- vue+webpack工程中怎样在vue页面中引入第三方非标准的JS库或者方法
方法一:异步加载第三方库 在我们的vue工程中新建如下路径:src/utils/index.js,在index.js中实现如下方法: export function loadScript(url) { ...
- js 翻译 c# 注意事项
1. split('') 在 c# 是不可以 .Split("") 的 要写 ToCharArray() 更新: 2021-09-25, split('') 是不好的写法, es6 ...
- SQL Server – Concurrency 并发控制
前言 以前写过相关的, 但这篇主要讲一下概念. 帮助理解 Entity Framework with MySQL 学习笔记一(乐观并发) Asp.net core 学习笔记 ( ef core tra ...