【Linux】Linux工具学习之gcc/g++

发布于:2024-03-27 ⋅ 阅读:(73) ⋅ 点赞:(0)

在这里插入图片描述
🔥博客主页 小羊失眠啦.
🎥系列专栏《C语言》 《数据结构》 《C++》 《Linux》 《Cpolar》
❤️感谢大家点赞👍收藏⭐评论✍️


在这里插入图片描述

接上文,我们已经学习了 Linux 中的编辑器 vim相关使用方法,现在已经能直接在 Linux 中编写C/C++代码,有了代码之后就要尝试去编译并运行它,此时就可以学习一下 Linux 中的编译器 gcc/g++ 了,我们一般使用 gcc 编译C语言,g++ 编译C++(当然 g++ 也可编译C语言),这两个编译器我们可以当作一个来学习,因为它们的命令选项都是通用的,只是编译对象不同。除了编译器相关介绍外,本文还会库、自动化构建工具、提权等知识,一起来看看吧

文章目录

  • 程序实现的两大环境
  • 一、gcc/g++使用
    • 1.1 gcc如何完成
      • 1.1.1 预处理
      • 1.1.2 编译
      • 1.1.3 汇编
      • 1.1.4 链接
  • 二、动态库与静态库
    • 2.1 动态库
    • 2.2 静态库
  • 三、自动化构建工具
    • 3.1 Makefile文件
    • 3.2 make指令
    • 3.3 任务刷新策略
    • 3.4 .PHONY伪目标
    • 3.5 补充
  • 四、sudo提权

程序实现的两大环境

任何一个C程序的实现都要经过翻译环境执行环境

在翻译环境中又分为4个部分,预编译、编译、汇编与链接。在各个阶段主要完成的任务有:

  1. 预编译(预处理):头文件的包含、注释的删除、#define符号的替换;

  2. 编译:将C语言代码转化为汇编代码;

  3. 汇编:把汇编指令转化为二进制指令;

  4. 链接:合并符号表和段表,生成可执行程序。

在这里插入图片描述

一、gcc/g++使用

1.1 gcc如何完成

通过gcc指令的不同选项可查看各阶段所形成的文件;

格式:gcc [选项] [目标文件名] -o [生成文件名]

首先在test.c文件中写好C代码:

#include<stdio.h>

#define N 100

//#define ...

int main()
{
	int n = 0;
	printf("Hello World\n");
	printf("%d\n", n + N);
	return 0;
}

1.1.1 预处理

查看 test.c 预处理后的结果,-E选项的作用是让 gcc 在预处理结束后停止编译过程;-o 的作用是将预处理后的内容保存到test.i 文件中。

gcc -E test.c -o test.i

在这里插入图片描述

如图所示,预处理阶段进行了头文件包含、注释的删除、#define的替换。

1.1.2 编译

接下来将刚刚生成的 test.i 进行编译,并在编译之后停下来,将结果写入 test.s 中。gcc所用选项为 -S。

gcc -S test.i -o test.s

在这里插入图片描述

我们虽然可能没学习过汇编语言,但依旧隐约认识到这些就是汇编代码;可见编译阶段就是将C代码翻译为汇编指令。

1.1.3 汇编

gcc所用选项为 -c;-o 将结果写入到 test.o 中。

gcc -c test.s -o test.o

在这里插入图片描述

正如我们所见,汇编完成之后都这这样的乱码。其实汇编之后,生成的文件为二进制文件,是用来给计算机看的,咱们已经看不懂了。

1.1.4 链接

编译完成之后就进入了链接阶段,链接完成之后就会生成可执程序 mytest了。

gcc test.o -o mytest

在这里插入图片描述

但是关于链接,我们需要知道它在链接什么。

我们是否好奇过为什么我们明明没有定义过函数 printf 、return …等等的函数,但却可以使用它的功能?

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


二、动态库与静态库

函数库一般分为动、静态库两种。

2.1 动态库

动态库 即通过 动态链接 的库,动态库 又称 共享库,因为 动态库 中的内容是被所有程序共享的,简言之 动态库 中的代码只需要存在一份,程序需要使用时,直接通过对应位置调用就行了

gcc 进行函数库的链接时可选择静态链接或者动态链接

Linux 中默认使用 动态链接 的方式,我们可以通过指令 ldd 最终生成的文件 来查看最终生成文件的链接情况

在这里插入图片描述

libXXX.so 是动态链接的标志

  • 其中 lib 是前缀
  • .so 是后缀
  • 去掉前缀与后缀,就是最终调用的库

举例:libc.so 去掉前缀与后缀,最终为 c ,可以看出文件最终调用的是C语言共享库,即 动态链接

动态链接 主要依赖不同函数在库中的位置信息进行调用,只有一份代码库,比较节省空间

我们还可以通过 file 命令查看文件详细信息

在这里插入图片描述

这也验证了 Linux 默认使用 动态链接 的现象

2.2 静态库

除了 动态库 外,还有静态库 ,采用静态链接的方式;静态链接不同与动态链接共享的方式,如果程序调用静态库,会将自己所需要的代码拷贝至程序中,完成拷贝后,后续不需要再调用 静态库

如果想采用 静态链接 链接的方式编译程序,需要在编译时加上 -static 选项,当然前提是得有 静态库,没有的可以通过 yum install -y glibc-static 下载 静态库

当然我们也可以通过 ldd 最终生成的文件 查看是否为 静态链接

#下载静态库
yum install -y glibc-static
#采取静态链接的方式编译程序
gcc -static test.c -o Test_static
#查看文件的链接方式
ldd Test_static

在这里插入图片描述

静态库 命名为 libXXX.a

  • lib 是前缀
  • .a 是后缀
  • 去掉前缀与后缀,就是最终调用的库

我们也可以采用 file 命令查看详细信息

file Test_static

在这里插入图片描述

静态链接 因为是直接将需要的代码拷贝到程序中,因此最终生成的文件会变大,比较占空间

对比二者生成的文件大小

#采用静态链接
gcc -static test.c -o Test_static
#默认采用动态链接
gcc  test.c -o Test

若在静态链接时出错,可能是因为你的 Linux 没有安装C语言的静态库,须手动安装。

在这里插入图片描述

两种连接方式生成的文件大小几乎相差百倍。

优劣对比

动态库静态库 各有优缺点,不然也不会同时存在两种库了

区别 动态库 静态库
调用方式 通过函数位置进行调用 直接将需要的函数拷贝至程序中
依赖性(运行时) 需要依赖于动态库 可以独立于静态库运行
空间占用 共享动态库中的代码,空间占用少 拷贝代码会占用大量空间
加载速度 调用函数,加载速度慢 直接运行,加载速度快

小结

动态库

  • 优点
    • 可以实现不同进程间的资源共享
    • 对于函数的升级只需要替换动态库文件,不需要重新编译程序
    • 可以控制是否加载动态库,不调用函数时就不加载
  • 缺点
    • 需要调用函数,加载速度较慢
    • 程序运行需要依赖动态库

静态库

  • 优点
    • 所需函数直接拷贝至程序中,运行速度快
    • 程序运行无需依赖库,便于移植
  • 缺点
    • 对于函数的升级,需要重新进行编译
    • 同一份代码可能出现重复拷贝的情况,浪费空间

三、自动化构建工具

自动化构建工具可以帮助我们完成设置好的指令,指令为 make ,我们可以通过提前设置,实现源文件的快速编译

3.1 Makefile文件

要想使用 make 指令,就得先有 Makefile 文件,Makefile 文件中主要编写任务,而任务由 依赖关系 + 依赖方法 构成

1.依赖关系

  • 比如源文件为 test.c ,编译后生成的文件为 mytest ,那么两者间的 依赖关系mytest:test.c 这组 依赖关系 我们可以写入 Makefile 文件中

2.依赖方法

  • 有了关系后,就要描述具体实现方法,比如上面那组 依赖关系依赖方法gcc test.c -o mytest依赖方法 也写入 Makefile 文件中

在这里插入图片描述

完成上面两个内容的编写后,我们就得到了一个基本的自动化任务,输入 make mytest 即可编译 test.c 文件,生成 mytest

#执行自动化指令,编译test.c文件
make mytest

在这里插入图片描述

注意: 同一个自动化任务,执行成功后,如果相关文件最近没有发生改变,那么无法再次执行自动化任务

3.2 make指令

上面展示了如何编写 Makefile 文件并执行相关任务,使用了 make file 指令,并没有直接使用 make指令,因为这个指令还是有些说法的

单纯输入 make 指令时,默认执行 Makefile 中的第一个任务,当任务成功执行后,不再继续执行后续任务(一个 Makefile 文件中,可以有多个任务),由此可见,单纯的 make 指令只会执行第一个自动化任务

在这里插入图片描述

在这里插入图片描述

当我们编写好 Makefile 文件后,可以通过 make 任务名 调用任务,任务名就是 依赖关系 中的左侧名;也可以直接通过 make 调用第一个任务

3.3 任务刷新策略

前面说过,同一个方法如果成功执行过,在原文件最近修改时间没有发生变化时,无法再执行任务,这背后的原因是方法是否执行会先判断生成的目标文件是否为最新,如果为最新,就不再执任务

举例:重复执行 make myfile 任务

make myfile    //第一次执行任务,成功
make myfile    //第二次执行任务,失败,因为源文件最近没有被修改

在这里插入图片描述

想要再次执行任务也很简单,对源文件做出修改,或者直接 touch 一下源文件就行了,两种行为都会修改文件的最近修改时间,使源目标文件不是最新时间

3.4 .PHONY伪目标

.PHONYMakefile 文件中的一个关键字,意为对某某对象生成伪目标,这样就能在不对源文件进行修改的情况下,重复执行任务了

//Makefile文件中
.PHONY:mytest

在这里插入图片描述

在这里插入图片描述

在使用关键字 .PHONY 对目标进行修饰后,可以无视任务刷新策略,重复执行任务了

不过这有什么意义呢?
答:对于这种源文件来说,没有任何意义

.PHONY 这个关键字,一般是用来修饰 clean 任务,即清理解决方案,Makefile 实现为

//Makefile 文件中
.PHONY:clean
clean:
	rm -r mytest

在这里插入图片描述

在这里插入图片描述

换个角度想想,当我们把生成的原目标文件清理后,再执行任务,生成目标文件是一件很合理的事,也完全符合任务刷新策略

由此来看,.PHONY 也是很有用的

注意: clean:这种半缺失 依赖方法 是合理的,毕竟清理这个任务也不需要任何对象,只需要单纯的执行删除(清理)指令就行了

3.5 补充

make 指令的工作原理是去 Makefile 文件中寻找任务执行,它的设计者为了确保普适性,创建 makefile 文件也是合法可用的

也就是说,我们创建 make 指令的任务源文件时,可以创建为 Makefile ,也可以创建为 makeile

四、sudo提权

权限,是一个让人又爱又恨的东西,它的安全性固然很重要,但有时候又太麻烦了,当我们普通用户想执行操作时,需要请 root 出马,比如最基本的下载软件指令,感觉有些小题大做了

为了解决这种不合理的现象,Linux 中就有 sudo 提权 这个概念,简单来说,就是暂时借助 root 的身份去完成某条指令

在这里插入图片描述

不过普通用户默认是没有赋予提权权限的,还是需要请 root 帮忙配置
步骤如下

  • 切换为 root 用户
  • 打开 /etc/sudoers 这个文件
  • 找到如下图所示区域,将需要提权的普通用户添加进去就行了

在这里插入图片描述

//root 身份下
vim /etc/sudoers    //打开这个配置文件,找到上图区域进行修改就行了

提权 配置完成后,普通用户遇到权限拒绝的场景时,只需要 sudo 指令 ,然后输入当前普通用户的密码,就可以暂时借助 root 的身份无视权限完成指令了

注意: sudo 后,输入的是当前普通用户的密码,不需要输入 root 密码,这样就能做到保护 root 的情况下,执行指令了

在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到