Linux基础开发工具二(gcc/g++,自动化构建makefile)

news2025/5/18 8:23:52

3. 编译器gcc/g++

3.1 背景知识


1. 预处理(进行宏替换/去注释/条件编译/头文件展开等)

2. 编译(生成汇编)

3. 汇编(生成机器可识别代码)

4. 连接(生成可执行文件或库文件)

3.2 gcc编译选项

格式 : gcc 【选项】 要编译的文件 【选项】【目标文件】

1.预处理(进行宏替换)

预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等。

预处理指令是以#号开头的代码行。

实例: gcc –E hello.c –o hello.i

选项“-E”,该选项的作⽤是让 gcc 在预处理结束后停⽌编译过程。

选项“-o”是指⽬标⽂件,“.i”⽂件为已经过预处理的C原始程序。

2. 编译(生成汇编)

在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,在检查⽆误后,gcc 把代码翻译成汇编语⾔。

用户可以使⽤“-S”选项来进⾏查看,该选项只进⾏编译⽽不进⾏汇编,⽣成汇编代码。

实例: gcc –S hello.i –o hello.s
 

3. 汇编(生成机器可识别代码)

汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件

读者在此可使⽤选项“-c”就可看到汇编代码已转化为“.o”的⼆进制⽬标代码了

实例: gcc –c hello.s –o hello.o

4. 连接(生成可执行文件或库文件)


3.3 动态链接和静态链接

在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件,为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接。静态链接的缺点很明显:

  1. 浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有

    printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;

  2. 更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程序。但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快

动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。

动态链接其实远⽐静态链接要常⽤得多。⽐如我们查看下 hello 这个可执⾏程序依赖的动态库,会发现它就⽤到了⼀个c动态链接库:

$ ldd hello
linux-vdso.so.1 => (0x00007fffeb1ab000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff776af5000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff776ec3000)

# ldd命令⽤于打印程序或者库⽂件所依赖的共享库列表。

在这里涉及到⼀个重要的概念: 库!!!

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,⽽没有定义函数的实现,那么,是在哪⾥实“printf”函数的呢?

答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。

如何理解库?
为什么要有库?让程序员直接使用,提高开发效率,语言上的库更多的时提供公共的方法集合(c语言提供的printf需要stdio.h, c++提供STL容器 需要iostream),printf(“hello world” )是打印到了显示器硬件上,是c语言工程师把printf往显示器上打印的功能写好了把他放在库里面你直接调用;使你不用外部的东西直接在语言当中就能编程使用语言提供的方法能在语言层面调用printf,STL容器之类)把搭建上层应用所需要的所有底层功能全部按技术角度提前写好放入库,让程序员直接使用,提高开发效率。

动态库/共享库最终也是会加载到内存中的,只有一份,把公共的方法抽取出来,在系统中只有一份节省内存资源,磁盘里面不存速度慢一点。


 动态链接就是程序还没加载到内存中时,就已经和动态库建立了连接;

动态链接:

程序通过连接器获取了连接信息,要进行链接!!

进行链接就是去动态库获取写入方法的地址;

优点:节省资源;

缺点:动态库一旦丢失所有程序无法直接运行、速度慢;


静态链接:  把你要的方法直接拷贝到可执行程序中

动态库把方法直接给了连接器,连接器进行链接时,直接给程序,和动态库取消联系,不依赖任何库

优点:不依赖任何库,自己独立就能运行;

缺点:体积大,占据资源多(占据磁盘空间,内存空间)无法充分利用资源,加载速度受影响

我们的系统中一般默认没有安装c/c++的静态库,我们可以用命令行安装c/c++的静态库:

使用yum(适用于CentOS,RHEL,Fedora)centos:

sudo yum install glibc-static

sudo yum install libstdc++-static

如果需要完整的的开发工具链(如gcc、g++等),可以安装一下包:

sudo yum install gcc gcc-c++ make

使用apt(适用于Ubuntu,Debian等):

sudo apt install libc6-dev

sudo apt install libstdc++-static-dev

如果需要完整的的开发工具链(如gcc、g++等),可以安装一下包:

sudo apt install build-essential

3.4 静态库和动态库


静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运⾏时也就不再需要库⽂件了。其后缀名⼀般为".a"

动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由运⾏时链接⽂件加载库,这样可以节省系统的开销。动态库⼀般后缀名为“.so”,如前⾯所述的libc.so.6 就是动态库。gcc 在编译时默认使⽤动态库。完成了链接之后,gcc 就可以⽣成可执⾏⽂件,如下所⽰。 gcc hello.o –o hello

gcc默认生成的⼆进制程序,是动态链接的,这点可以通过 file 命令验证。
 

Linux下,动态库XXX.so, 静态库XXX.a

Windows下,动态库XXX.dll, 静态库XXX.lib

如何查看是否已经安装epel-release源:

EPEL 仓库的配置文件通常位于 /etc/yum.repos.d/ 目录下。你可以列出该目录下的文件,看看是否有与 EPEL 相关的 repo 文件:

ls -l /etc/yum.repos.d/ | grep epel

也可以安装一个广泛使用的C++库集合,提供了大量的功能,从智能指针到正则表达式支持等

# 安装 Boost 静态库
sudo yum install boost-devel

二. 自动化构建 -make/Makefile

2.1 背景

会不会写makefile,从⼀个侧⾯说明了⼀个人是否具备完成⼤型工程的能力。

⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作

makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全⾃动编译,极⼤的提⾼了软件开发的效率。

make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这个命令,⽐如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可⻅,makefile都成为了⼀种在⼯程⽅⾯的编译⽅法。

make是⼀条命令,makefile是⼀个文件,两个搭配使⽤,完成项目自动化构建

2.2 基本使用 

实例代码
 

#include <stdio.h>
int main()
{
printf("hello Makefile!\n");
return 0;
}

Makefile文件

myproc:myproc.c
gcc -o myproc myproc.c
.PHONY:clean
clean:
rm -f myproc

依赖关系

上⾯的⽂件myproc,它依赖myproc.c

依赖方法

gcc -o myproc myproc.c ,就是与之对应的依赖关系

项⽬清理

⼯程是需要被清理的

像clean这种,没有被第⼀个⽬标⽂件直接或间接关联,那么它后⾯所定义的命令将不会被⾃动执⾏,不过,我们可以显⽰要make执⾏。即命令⸺“make clean”,以此来清除所有的⽬标⽂件,以便重编译。

但是⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤ .PHONY 修饰,伪⽬标的特性是,总是被执⾏的。

这里就涉及到了总是被执行和不被执行两个概念。

这里直接看图详解:

 

输入make命令,make命令会自动在当前目录下 找Makefile,然后用make去解释Makefile里面的内容, 从上往下执行Makefile中的编译方法,帮我们形成可执行程序。

有了.PHONY,make clean 就可以重复执行 rm -f code。     

.PHONY:让make忽略源文件和可执行标文件的M时间对比
 

2.3 推导过程 

 

myproc:myproc.o
gcc myproc.o -o myproc

myproc.o:myproc.s
gcc -c myproc.s -o myproc.o

myproc.s:myproc.i
gcc -S myproc.i -o myproc.s

myproc.i:myproc.c
gcc -E myproc.c -o myproc.i

.PHONY:clean
clean:
rm -f *.i *.s *.o myproc

编译过程 

$ make
gcc -E myproc.c -o myproc.i
gcc -S myproc.i -o myproc.s
gcc -c myproc.s -o myproc.o
gcc myproc.o -o myproc

 

make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么:

  1. make会在当前⽬录下找名字叫“Makefile”或“makefile”的⽂件。
  2. 如果找到,它会找⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会找到 myproc 这个⽂件,并把这个⽂件作为最终的⽬标⽂件。
  3. 如果 myproc ⽂件不存在,或是 myproc 所依赖的后⾯的 myproc.o ⽂件的⽂件修改时间要⽐ myproc 这个⽂件新(可以⽤ touch 测试),那么,他就会执⾏后⾯所定义的命令来⽣成myproc 这个⽂件。
  4. 如果 myproc 所依赖的 myproc.o ⽂件不存在,那么 make 会在当前⽂件中找⽬标为myproc.o ⽂件的依赖性,如果找到则再根据那⼀个规则⽣成 myproc.o ⽂件。(这有点像⼀个堆栈的过程)
  5.  当然,你的C⽂件和H⽂件是存在的啦,于是 make 会⽣成 myproc.o ⽂件,然后再⽤ myproc.o ⽂件声明 make 的终极任务,也就是执⾏⽂件 hello 了。
  6. 这就是整个make的依赖性,make会⼀层⼜⼀层地去找⽂件的依赖关系,直到最终编译出第⼀个⽬标⽂件。
  7.  在找寻的过程中,如果出现错误,⽐如最后被依赖的⽂件找不到,那么make就会直接退出,并报错,⽽对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后⾯的⽂件还是不存在,那么就停止工作


 

输入make命令时,会读取当前目录下的Makefile文件,自顶向下对文件进行扫描,首先会发现code依赖的code.o不存在,因为code.o也有依赖关系,所以make进而找code.o的依赖关系,依次往下。到最后找到code.i依赖的code.c,依赖文件列表code.c本身在当前目录下是已经存在的,所以code.c已经具备了能够进行形成code.i的条件了,所以make就会执行code.i的依赖方法gcc -E code.c -o code.c这条命令,一旦code.i形成那么code.s,code.s所对应的依赖关系也有了,就会执行他的依赖方法。依次往上。---这就是make自动推导的过程。

当识别到code.o不存在会向下找,因为code.o也有依赖关系,所以make除了找code.o的依赖关系,还会把上一组code.o形成code的这组依赖方法入栈,然后依次往下,等到识别到code.c已经具备了能够进行形成code.i的条件时,然后依次出栈执行依赖方法。

不想看这个命令执行的过程,只看结果,前面加@,关闭回显

 

2.4 扩展语法

有了变量的定义,我们要定义的目标文件叫Bin,

源文件SRC

BIN=proc.exe         # 定义变量
CC=gcc
#SRC=$(shell ls *.c) # 采⽤shell命令⾏⽅式,获取当前所有.c⽂件名
SRC=$(wildcard *.c)  # 或者使⽤ wildcard 函数,获取当前所有.c⽂件名
OBJ=$(SRC:.c=.o)     # 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表
LFLAGS=-o            # 链接选项
FLAGS=-c             # 编译选项
RM=rm -f             # 引⼊命令

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全局异常处理:如何优雅地统一管理业务异常

在软件开发中&#xff0c;异常处理是保证系统健壮性的重要环节。一个良好的异常处理机制不仅能提高代码的可维护性&#xff0c;还能为使用者提供清晰的错误反馈。本文将介绍如何通过全局异常处理和业务异常统一处理来编写更加优雅的代码。 一、传统异常处理的痛点 1.1 典型问…

动态规划-LCR 166.珠宝的最大价值-力扣(LeetCode)

一、题目解析 frame二维矩阵中每个值代表珠宝的价值&#xff0c;现在从左上角开始拿珠宝&#xff0c;只能向右或向下拿珠宝&#xff0c;到达右下角时停止拿珠宝&#xff0c;要求拿的珠宝价值最大。 二、算法解析 1.状态表示 我们想要知道的是到达[i,j]为位置时的最大价值&am…

JDBC实现模糊、动态与分页查询的详解

文章目录 一. 模糊查询1. Mysql的写法2. JDBC的实现 二. 动态条件查询1. 创建生成动态条件查询sql的方法2. 完整的动态条件查询类以及测试类 三. 分页查询1. 什么是分页查询&#xff1f;2. 分页查询的分类3. MySQL的实现4. JDBC实现4.1. 创建page页4.2. 分页的实现 本章来讲一下…

域环境信息收集技术详解:从基础命令到实战应用

引言 在企业网络环境中&#xff0c;Active Directory (AD)域服务是微软提供的集中式目录服务&#xff0c;用于管理网络中的用户、计算机和其他资源。对于信息安全专业人员来说&#xff0c;熟练掌握域环境信息收集技术至关重要&#xff0c;无论是进行渗透测试、安全评估还是日常…

【C++ Qt】布局管理器

每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” &#x1f914;绪论​&#xff1a; 在Qt开发中&#xff0c;界面布局的合理设计是提升用户体验的关键。早期&#xff0c;开发者常采用绝对定位的方式摆放控件&#xff0c;即通…

vscode用python开发maya联动调试设置

如何在VScode里编写Maya Python脚本_哔哩哔哩_bilibili1 包括1&#xff0c;maya的python全面在vscode支持&#xff0c;2&#xff0c;通过mayacode发送到maya&#xff0c;3同步调试 import maya.cmds as cmds 1、让 maya.cmds编译通过 下载Autodesk_Maya_2018_6_Update_DEVK…

SLAM定位常用地图对比示例

序号 地图类型 概述 1 格栅地图 将现实环境栅格化,每一个栅格用 0 和 1 分别表示空闲和占据状态,初始化为未知状态 0.5 2 特征地图 以点、线、面等几何特征来描绘周围环境,将采集的信息进行筛选和提取得到关键几何特征 3 拓扑地图 将重要部分抽象为地图,使用简单的图形表示…

python的漫画网站管理系统

目录 技术栈介绍具体实现截图![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0ed2084038144499a162b3fb731a5f37.png)![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a76a091066f74a80bf7ac1be489ae8a8.png)系统设计研究方法&#xff1a;设计步骤设计流程核…

源码安装gperftools工具

源码安装gperftools工具 下载gperftools源码 https://github.com/gperftools/gperftools/releases/download/gperftools-2.16/gperftools-2.16.tar.gz 注&#xff1a;需要下载github上release版本&#xff0c;如果直接下载master分支上源码&#xff0c;将可能出现各种编译报错…

前端脚手架开发指南:提高开发效率的核心操作

前端脚手架通过自动化的方式可以提高开发效率并减少重复工作&#xff0c;而最强大的脚手架并不是现成的那些工具而是属于你自己团队量身定制的脚手架&#xff01;本篇文章将带你了解脚手架开发的基本技巧&#xff0c;帮助你掌握如何构建适合自己需求的工具&#xff0c;并带着你…

搜索引擎工作原理|倒排索引|query改写|CTR点击率预估|爬虫

写在前面 使用搜索引擎是我们经常做的事情&#xff0c;搜索引擎的实现原理。 什么是搜索引擎 搜索引擎是一种在线搜索工具&#xff0c;当用户在搜索框输入关键词时&#xff0c;搜索引擎就会将与该关键词相关的内容展示给用户。比较大型的搜索引擎有谷歌&#xff0c;百度&…

Python实例题:Python自动工资条

目录 Python实例题 题目 python-automatic-payroll-slipPython 自动生成工资条脚本 代码解释 加载文件&#xff1a; 获取表头&#xff1a; 写入表头&#xff1a; 生成工资条&#xff1a; 保存文件&#xff1a; 运行思路 注意事项 Python实例题 题目 Python自动工资…

Function Calling万字实战指南:打造高智能数据分析Agent平台

个人主页&#xff1a;Guiat 归属专栏&#xff1a;科学技术变革创新 文章目录 1. Function Calling&#xff1a;智能交互的新范式1.1 Function Calling 技术概述1.2 核心优势分析 2. 数据分析Agent平台架构设计2.1 系统架构概览2.2 核心组件解析2.2.1 函数注册中心2.2.2 Agent控…

线对板连接器的兼容性问题:为何老旧设计难以满足现代需求?

线对板连接器作为电子设备的核心纽带&#xff0c;正面临前所未有的兼容性挑战。某智能工厂升级生产线时发现&#xff0c;沿用十年的2.54毫米间距连接器&#xff0c;在接入新型工业相机时出现30%的信号丢包率&#xff0c;而切换至0.4毫米超密间距连接器后&#xff0c;数据传输速…

AI517 AI本地部署 docker微调(失败)

本地部署AI 计划使用OLLAMA进行本地部署 修改DNS 访问github 刷新缓存 配置环境变量 OLLAMA安装成功 部署成功 计划使用docker进行微调 下载安装docker 虚拟化已开启 开启上面这些 准备下载ubuntu docker ragflow dify 用git去泡

VR和眼动控制集群机器人的方法

西安建筑科技大学信息与控制工程学院雷小康老师团队联合西北工业大学航海学院彭星光老师团队&#xff0c;基于虚拟现实&#xff08;VR&#xff09;和眼动追踪技术实现了人-集群机器人高效、灵活的交互控制。相关研究论文“基于虚拟现实和眼动的人-集群机器人交互方法” 发表于信…

TiDB 中新 Hash Join 的设计与性能优化

原文来源&#xff1a; https://tidb.net/blog/11667c37 本文作者&#xff1a;徐飞 导读 在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;连接操作&#xff08;Join&#xff09;是查询处理的核心环节之一&#xff0c;其性能直接影响到整个系统的响应速度和效率…

1.共享内存(python共享内存实际案例,传输opencv frame)

主进程程序 send.py import cv2 import numpy as np from multiprocessing import shared_memory, resource_trackercap cv2.VideoCapture(0) if not cap.isOpened():print("无法打开 RTSP 流&#xff0c;请检查地址、网络连接或 GStreamer 配置。") else:# 创建共…

网页常见水印实现方式

文章目录 1 明水印技术实现1.1 DOM覆盖方案1.2 Canvas动态渲染1.3 CSS伪元素方案2 暗水印技术解析2.1 空域LSB算法2.2 频域傅里叶变换3 防篡改机制设计3.1 MutationObserver防护3.2 Canvas指纹追踪4 前后端实现对比5 攻防博弈深度分析5.1 常见破解手段5.2 进阶防御策略6 选型近…

【ARM】MDK如何将变量存储到指定内存地址

1、 文档目标 在嵌入式系统开发中&#xff0c;通过MDK&#xff08;Microcontroller Development Kit&#xff09;进行工程配置&#xff0c;将指定的变量存储到指定的内存地址上是一项非常重要的技术。这项操作不仅能够满足特定硬件架构的需求&#xff0c;还能优化系统的性能和…