从编译链接到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虚拟机栈是线程私有的,它的生命周期和线程同步 一个线程每执行到一个方法,JVM就会创建一个栈帧(用于存储基本数据类型.对象指针和返回值等),并将栈帧压入栈中. 代码示例: public cla ...
- disconf分布式配置管理(二) 与spring集成
上一章介绍了disconf的安装预配置,这章主要介绍下disconf与spring集成 1.添加依赖 <dependency> <groupId>com.baidu.disco ...
- games101 作业4及作业5 详解光线追踪框架
games101 作业4及作业5 详解光线追踪框架 作业4 代码分析 作业四的代码整体比较简单 主要流程就是 通过鼠标事件 获取四个控制点的坐标 然后绘制贝塞尔曲线的内容就由我们来完成 理论分析 贝塞 ...
- AREA |.text|, CODE, READONLY, ALIGN=2详解
AREA |.text|, CODE, READONLY, ALIGN=2 ;AREA |.text| 选择段 |.text|. ;CODE表示代码段,READONLY表示只读(缺省) ...
- docker部署宝塔面板
环境准备: 系统 rocky:9.2 部署流程: 1.安装docker dnf -y install yum-utils yum-config-manager --add-repo http://mi ...
- 关于为什么使用 ASCII GBK Unicode编码
关于为什么使用 ASCII GBK Unicode编码 由来:大家都知道计算机最早是美国人为了更加便捷的存储和计算数据发明的,但是呢计算机底层都是硬件,只能存储像0101这样的二进制数据,那美国人为了 ...
- udp协议及包格式
UDP协议也是互联网基础协议之一.它和TCP一样同属于传输层当中的一个协议. 不过UDP协议是一个面向无连接的协议(TCP是向面连接的协议).一个UDP连接的建立,不必象TCP协议那样需要服务器端侦听 ...
- 【YashanDB数据库】yasboot查询数据库状态时显示数据库状态为off
[问题现象] yasboot cluster status -c yashandb 显示数据库状态为off与数据库实际的状态不符,如下图 [问题分类]yasboot.yasdb使用问题 [关键字]ya ...
- 5.5文件上传-WAF绕过
一.WAF绕过(明确有文件上传) 1.上传参数中,可修改参数 Content-Dispositin:一般可改 name:表单参数,不可更改 filename:文件名,可更改 Content-Type: ...
- vue3 modifier
vue3 modifier 是什么? modifier 中文意思为修饰符. 在vue3中主要是体现在v-model上,vue3允许我们添加自定义修饰符. 比如说这样: <template> ...