从编译链接到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 ...
随机推荐
- Unreal使用GooglePAD生成AAB包,并加在fast-follow资源
1.修改obbfilter,设置需要添加到obb的pak文件 2.修改项目设置,打AAB包 3.cook stage生成所有Paks文件 4.将部分pak文件拷贝到Intermediate/Andro ...
- TeX Live 安装
Ubuntu sudo apt install texlive-full 其他可用软件包: 软件包 压缩包 磁盘空间 texlive-latex-base 59 MB 216 MB texlive-l ...
- C# – 10.0
前言 之前写过 6.0, 7.0, 8.0, 9.0 总结. 10.0 也是有些好东西哦, 尤其是 pattern matching 的完善, 差不多是时候可以重构 if else switch 的写 ...
- LLM应用实战: 文档问答系统Kotaemon-1. 简介及部署实践
1.背景 本qiang~这两周关注到一个很火的开源文档问答系统Kotaemon,从8月28日至今短短两周时间,github星标迅猛增长10K,因此计划深挖一下其中的原理及奥秘. 本篇主要是Kotaem ...
- 应聘软件测试 HR 会问到哪些问题?收藏这一篇就够了!
1.你还有收到其他offer吗? 其实hr问你offer情况,是对你感兴趣,想要进一步了解你,看下你的市场竞争力. 但注意不要太坦诚的说:我还没有offer或者收到两个offer还想对比对比:也不要撒 ...
- IT男如何走上的自由职业之路。
前言 在博客园的一篇文章<40岁大龄失业程序猿,未来该何去何从>,在下面留言,目前自己在做自由职业,很多人加好友咨询自由职业的事情,对IT行业自由职业比较感兴趣,问怎么能走上这条路,所以才 ...
- [29] CSP模拟2
A.不相邻集合 考虑值域上连续的段,可以发现连续的长度为 \(x\) 的段的贡献必定为 \(\lceil{\frac{x}{2}}\rceil\) 考虑并查集维护值域连续的段的大小,每次询问求出全部连 ...
- VB.NET 在 Windows下通过WIn32API获取CPU和内存的使用率
.net 要获取CPU和内存的使用率,一般是通过 PerformanceCounter 或者 WMI 查询得到,但是如果操作系统经常不正常断电或者别的什么原因,让系统的性能计数器抽风了,可能就会造成初 ...
- urb中几个函数的使用
usb_buffer_alloc(free) 说是为了更好的从名字看出这个函数真实做的事情:DMA coherency linux提供两种方式,来保证使用dma时,内存和硬件cache的一致性: us ...
- emmc寿命
EMMC器件寿命 1)先确认EMMC器件NAND FLASH类型,是MLC还是TLC,一般是TLC,器件手册标称1000-3000次,取平均值2000次作为评估: 2)在OS下查看EMMC器件当前使用 ...