【Linux系列】打造你的数字车间:Linux 基础开发工具入门与精要 — gcc/g++ 编译

【Linux系列】打造你的数字车间:Linux 基础开发工具入门与精要 — gcc/g++ 编译
在这里插入图片描述



🫧 励志不掉头发的内向程序员个人主页
 ✨️ 个人专栏: 《C++语言》《Linux学习》

🌅偶尔悲伤,偶尔被幸福所完善


👓️博主简介:

在这里插入图片描述


文章目录


前言

上一章节讲解了 vim 编辑器,但是还没有说明我们的文件怎么编译成可执行文件,本章节我们便来讲解说明,这也是一个非常重要的内容,我们一起来看看吧。
在这里插入图片描述

一、编译流程

我们编译一般涉及 4 个流程,每个流程都有其作用,缺一不可。
  • 预处理(进行宏替换/去注释/条件编译/头文件展开等)
  • 编译(生成汇编)
  • 汇编(生成机器可识别代码)
  • 链接(生成可执行文件或库文件)

二、gcc 编译

gcc编译的作用就是用来编译我们 C 语言的文件的。编译时我们可以直接编译成可执行文件,也可以按照编译流程一步一步的编译。

2.1、直接生成可执行程序的 2 种方式

  • gcc [ 要编译的文件 ] -o [ 目标文件 ]
-rw-rw-r-- 1 zxl zxl 304 Sep 25 10:26 code.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.c -o mycode -rwxrwxr-x 1 zxl zxl 16000 Sep 25 11:24 mycode* zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ./mycode hello world!
  • gcc -o [ 目标文件 ] [ 要编译的文件1 ] [ 要编译的文件2 ] [ 要编译的文件3 ] …
-rw-rw-r-- 1 zxl zxl 304 Sep 25 10:26 code.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -o mycode code.c -rwxrwxr-x 1 zxl zxl 16000 Sep 25 11:28 mycode* zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ./mycode hello world!

2.2、gcc 编译选项

格式: gcc [ 选项 ] 要编译的文件 [ 选项 ] [ 目标文件 ]

预处理(进行宏替换)

  • 预处理功能主要包括宏定义、文件包含、条件编译、去注释等
  • 预处理指令是以 # 号开头的代码行
  • 选项 “ -E ”,该选项的作用是让 gcc 在预处理结束后停止编译过程
  • 选项 “ -o ” 是指目标文件,“ .i ” 文件为已经预处理过的 C 原始程序

例如:
代码:

// code.c// 头文件#include<stdio.h>// 宏替换#defineM100#defineNintmain(){printf("hello world!, %d\n", M);printf("hello world!\n");// 注释//printf("hello world!\n");//printf("hello world!\n");//printf("hello world!\n");printf("hello world!\n");// 条件编译#ifdefNprintf("hello N\n");#elseprintf("hello no N\n");#endifreturn0;}

编译(预处理):

-rw-rw-r-- 1 zxl zxl 368 Sep 25 11:47 code.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -E code.c -o code.i -rw-rw-r-- 1 zxl zxl 18053 Sep 25 11:48 code.i 

我们的 code.i 文件就是我们源文件在编译时预处理结束后就停止编译的文件。

在这里插入图片描述


此时我们预处理结束后头文件打开了,同时的宏被替换了,注释被去除的同时条件编译也执行了,所以就会成为上面的效果。

编译(生成汇编)

  • 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言
  • 用户可以使用 “ -S ” 选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码

编译(生成汇编):
我们可以拿 .c 文件直接进行生成汇编,也可以把已经预处理完成后的文件 .i 进行生成汇编。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -S code.i -o code.s # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -S code.c -o code.s -rw-rw-r-- 1 zxl zxl 924 Sep 25 13:48 code.s 

此时我们的代码就会被加工成汇编语言,同理 -S 只会把代码完成到生成汇编这一步,生成汇编后就停止下来了。

在这里插入图片描述



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

  • 汇编阶段是把编译阶段生成的 “ .s ” 文件转成目标文件
  • 读者在此可使用选项 “ -c ” 就可看到汇编代码已转化为 “ .o ” 的⼆进制目标代码

编译(生成机器可识别代码):
同理,我们可以拿汇编之前生成的 .c、.i、.s 文件进行汇编。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code.c -o code.o # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code.i -o code.o # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code.s -o code.o -rw-rw-r-- 1 zxl zxl 1752 Sep 25 13:54 code.o 

此时我们的代码就会被加工成机器可识别代码,同理 -c 只会把代码完成到这一步就停止下来了。

在这里插入图片描述


此时我们的文件就已经变成二进制文件了。但是我们还没有办法运行,这是因为我们 .o 文件只不过是把我写的代码变成二进制文件了。但是我的代码中用到的各种头文件和各种库函数等,都没有编译进去,所以我们还得有一个连接阶段。

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

  • 在成功编译之后,就进入了链接阶段

编译(生成可执行文件或库文件):

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.c -o code # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.i -o code # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.s -o code # 或者 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.o -o code -rwxrwxr-x 1 zxl zxl 16000 Sep 25 14:02 code* zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ./code hello world!, 100 hello world! hello world! hello N 

三、g++ 编译

gcc 编译是给我们 C 语言文件进行编译的, g++ 编译可以给我们 C 语言和 C++文件进行编译,它的编译方法和我们 gcc 一样,我们只要把前面的 gcc 变成 g++ 即可,这里就不过多赘述了。

四、静态库和动态库

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名⼀般为 “ .a ”。
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为 “ .so ”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件。

有了这些库以后,我们想要去实现很多常用的功能,就没有必要从零开始造轮子,会有人把这些功能为我们写好,放到库中供我们使用。这样也统一了我们各功能,使其不会一个功能却有成千上百中实现方式。

Linux 当中,肯定也会提前安装很多动静态库,存在 /usr/lib64 目录文件中。

在这里插入图片描述


注意:
一个库的真实名字是它的名字去掉前面的 lib,再去掉 . 后面的后缀,剩下的就是库的真实名字了。

五、动态链接和静态链接

在我们的实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的,即每个 *.c 文件会形成一个 *.o ⽂件,为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接。

静态链接的缺点很明显:

  • 浪费空间:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标⽂件都有依赖,如多个程序中都调用了 printf() 函数,则这多个程序中都含有 printf.o,所以同一个目标文件都在内存存在多个副本
  • 更新比较困难:因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

静态链接的优点是:

  • 在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快
在这里插入图片描述

动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。动态链接只需要能够找到库中的对应方法的地址,即可让我们的方法调用起来。

在这里插入图片描述

动态链接其实远比静态链接要常用得多。比如我们查看下 code 这个可执行程序依赖的动态库,会发现它就⽤到了⼀个 C 动态链接库。
在 Linux 中可以使用 ldd [可执行文件名] 来查看我们的这个文件依赖什么库以及是什么链接。

-rwxrwxr-x 1 zxl zxl 16000 Sep 25 14:02 code* zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ldd code linux-vdso.so.1 (0x00007ffdcb7f2000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd90923b000)/lib64/ld-linux-x86-64.so.2 (0x00007fd909471000)

这里面 libc.so.6 就是 C 的动态库,也叫 C 标准库。它的链接方式就是动态链接。

还可以使用 file [ 可执行文件名 ] 的指令来查看。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ file code code: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=73b57ba77bf13d1b9f3cee616c44215760563964,for GNU/Linux 3.2.0, not stripped 

从它的返回的字符串可以看到:

  • 64-bit:64位的
  • dynamically linked:动态链接

同时我们看动态库的文件大小为 16000。我们来看看如果是静态链接我们的文件大小会发生什么改变以及 file 和 ldd 会变成什么样子。
想要进行静态链接我们得在我们 gcc 命令后面加一个 -static 选项。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.c -o code -static -rwxrwxr-x 1 zxl zxl 900440 Sep 25 16:50 code*

我们可以看到我们的文件会变大非常多,将近 100 倍。这只是用了一个库的情况,要是有很多库,那就会非常浪费空间了。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ldd code not a dynamic executable 

此时我们 ldd 后会发现它说我们不是一个动态可执行。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ file code code: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=6c381730b960e1b3426a6071c0283120d7bcef6e,for GNU/Linux 3.2.0, not stripped 

file 后显示 statically linked(静态链接)的 executable(可执行程序)。

注意:
我们之所以能够使用动静态库是因为我们 Linux 早就预装了这些库在我们 /usr/lib64 目录下,如果没有预装库,那就会报错,解决办法就是去网上找到这些库然后去安装,这也就是为什么有的时候我们直接复制别人网上的代码却发现运行不了的原因。

我们说了动静态库的和我们的函数的链接,那我们如何将我们自己写的库和我们的函数链接呢?

其实很简单,让我们的库和我们的函数一起形成一个可执行文件即可。如下我们把 code1.h、code1.c、code2.h、code2.c 和我们 code.c 文件进行链接形成可执行文件。

-rw-rw-r-- 1 zxl zxl 0 Sep 25 17:16 code1.h -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:16 code1.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:16 code2.h -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:16 code2.c -rw-rw-r-- 1 zxl zxl 368 Sep 25 11:47 code.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code1.c code2.c code.c -o code -rwxrwxr-x 1 zxl zxl 16000 Sep 25 17:17 code*

当然,如果你不想让别人看到你的源文件,就比如你把这个代码给室友,让其完成作业,但是你不想让他知道你的代码是怎么实现的,此时你可以把你的 * .c 文件把它编译成 * .o 文件,然后让 * .o 文件和别的文件进行链接,这样就不会被别人知道你的代码是怎么实现的了(*.h 不用加密,因为如果加密了别人就不知道怎么使用你写的函数了)。

-rw-rw-r-- 1 zxl zxl 0 Sep 25 17:27 code1.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:28 code1.h -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:27 code2.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:28 code2.h -rw-rw-r-- 1 zxl zxl 368 Sep 25 11:47 code.c # 不写 -o 已经后面的目标文件,系统会自动生成要编译的文件名加相应的后缀的文件 zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code1.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code2.c zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc -c code.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:27 code1.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:28 code1.h -rw-rw-r-- 1 zxl zxl 936 Sep 25 17:29 code1.o -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:27 code2.c -rw-rw-r-- 1 zxl zxl 0 Sep 25 17:28 code2.h -rw-rw-r-- 1 zxl zxl 936 Sep 25 17:29 code2.o -rw-rw-r-- 1 zxl zxl 368 Sep 25 11:47 code.c -rw-rw-r-- 1 zxl zxl 1752 Sep 25 17:36 code.o zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ gcc code.o code1.o code2.o -o code -rwxrwxr-x 1 zxl zxl 16064 Sep 25 17:37 code*


总结

我们动静态库在后面会有一章用来专门讲解,所以大家能够大致了解即可,这里我们能够知道我们写的代码文件能够如何进行编译形成可执行的文件即可。剩下的我们后面会来讲解的,我们下一章节再见。

🎇坚持到这里已经很厉害啦,辛苦啦🎇ʕ • ᴥ • ʔづ♡ど

Read more

Z-Image-Turbo镜像推荐:Gradio WebUI免配置快速上手教程

Z-Image-Turbo镜像推荐:Gradio WebUI免配置快速上手教程 你是不是也遇到过这些情况:想试试最新的AI绘画模型,结果卡在环境搭建上——下载权重动辄几十GB、配置CUDA版本让人头大、改配置文件改到怀疑人生?或者好不容易跑起来了,WebUI界面又丑又难用,中文提示词还乱码?别折腾了。今天要介绍的这个镜像,真的做到了“点开即用”。 Z-Image-Turbo不是又一个参数堆砌的玩具模型,而是阿里通义实验室实打实打磨出来的高效文生图方案。它不靠堆显存换效果,而是用蒸馏技术把大模型的精华“浓缩”出来——8步出图、照片级质感、中英文文字渲染稳得一批,16GB显存的消费级显卡就能扛住。更重要的是,它被完整集成进了一个开箱即用的ZEEKLOG镜像里,连Gradio界面都给你调好了配色和字体,连“怎么输入中文”这种细节都考虑到了。 这篇文章不讲原理推导,不列参数表格,也不让你手动clone仓库、pip install一堆包。我们就用最直白的方式,带你从零开始,在5分钟内看到第一张由Z-Image-Turbo生成的高清图像。你不需要懂Diffusers,不需要会调acceler

By Ne0inhk
Gemini 无损去水印神器:基于数学算法的纯前端解决方案

Gemini 无损去水印神器:基于数学算法的纯前端解决方案

🎯 Gemini 无损去水印神器:基于数学算法的纯前端解决方案 🔗 项目地址:gemini-watermark-remover 🌐 在线体验:banana.ovo.re ⭐ 如果觉得有用,请给项目点个 Star! 📖 引言 随着 Google Gemini AI 图像生成功能的普及,越来越多的用户开始使用它来创作各种精美的图片。然而,Gemini 生成的图片右下角都会带有一个半透明的水印 Logo,这在某些场景下可能会影响图片的使用效果。 今天给大家推荐一个开源项目 gemini-watermark-remover,它能够完美无损地移除 Gemini 图片上的可见水印,而且完全在浏览器端运行,无需上传图片到服务器,充分保护用户隐私! ✨ 核心特性 🔒 100% 客户端处理,隐私至上 * 无需后端服务器:所有图片处理都在浏览器本地完成 * 零数据上传:图片永远不会离开你的设备 * 即开即用:打开网页即可使用,无需注册登录 🎯 数学精确,非 AI 模型 * 基于反向 Alpha 混合算法(

By Ne0inhk

安卓系统Chrome内核:Android System WebView

com.google.android.webview 安卓8.0可以使用Android System WebView v138 安卓7.0可以使用Android System WebView v119 安卓6.0可以使用Android System WebView v106 安卓5.0可以使用Android System WebView v95 网盘下载1:https://down666.lanzoul.com/b01hjlghc 提取码:7x8i ------旧版网盘下载1:https://down666.lanzoul.com/b01hjlgje 提取码:aw3t 网盘下载2:https://www.mediafire.com/folder/cimpgytm5w2t8 有的安卓浏览器比如“X浏览器”自身是不带Chrome内核的,

By Ne0inhk
基于java Web 健身房注册管理系统设计与实现

基于java Web 健身房注册管理系统设计与实现

博主介绍:翰文编程 专注于Java(springboot ssm 等开发框架) vue  .net  php phython node.js    uniapp 微信小程序 等诸多技术领域和课设项目实战、企业信息化系统建设,从业十八余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了2000+题目解决方法案例  方便大家学习使用 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人 文末下方有源码获取地址 4系统概要设计 4.1系统功能模块设计 系统主要功能各功能结构图模块如图4-1所示:                                     图4-1 功能结构图 4.2数据库设计 4.2.1数据库设计原则 4.2.2数据库表的设计 根据需求分析,本健身房平台的数据库表,具体设计如下: 1 会员( 会员编号,用户名,密码,姓名,

By Ne0inhk