gcc/g++
一般写好的代码就是用gcc来去编译
形成指定名称的可执行语句
在Linux中,gcc -o 用于指定输出的文件名称。
当使用gcc编译器进行编译时,-o 参数允许用户自定义生成的可执行文件的名称。
例如,如果用户执行命令 gcc -o ABC.o hello.c,这将编译 hello.c 文件并将生成的可执行文件命名为 ABC.o。
此外,C语言还可以用g++来进行编译和命名出可执行文件
g++ code.c -o 1.exe
g++ -o 2.exe code.c
C++语言要用g++来去编译,不能用gcc编译,否则会报错
编译原理
编译器的前世今生
汇编语言第一次用的编译器是用二进制写的,后来才慢慢用汇编语言编译出新的用汇编语言的编译器
C语言和用C语言写的编译器,哪个更早
肯定是C语言早,没有C语言的时候,有的是汇编语言写的汇编器
再用C语言写一个C语言的编译器(软件),交给汇编语言的编译器去编译,形成新的编译器,以后才能使用
即用来编译C语言的编译器是作为一个软件,先用C语言写出来,交给汇编语言编译器编译后形成
预处理
预处理
gcc -E 源文件 -o 目标文件
如果没有-o的话,会把很多内容打印在终端上,极其不方便
-E:从现在开始进行程序的翻译过程,当预处理做完的时候,就停下来!
修改一下code.c
执行预处理语句:gcc -E code.c -o code.i
打开预处理执行的文件,对比
多出来的800多行代码都是stdio.h展开的
所谓的头文件展开,本质是指在预处理的时候,将头文件的内容拷贝至源文件,但不是全部拷贝
预处理的三个我们已经了解,那条件编译呢
条件编译在实现代码的动态裁剪,防止头文件重复包含中起的作用比较大
编译
汇编语句跟预处理语句没有什么区别
gcc -S code.i -o code.s 生成 .s 文件
这里可以从源文件开始汇编,也可以从预处理文件开始编,没什么两样
汇编
gcc -c code.s (-o code.o) 生成 .o文件
汇编语言生成二进制文件,用文本编辑器打开会变成乱码
-c 的意思是从现在开始进行翻译工作
code.o是不能被运行的
由于目标文件会有很多的函数,很多函数都不是我们自己写的,所以我们需要调,预处理展开了头文件,所以我们要去跟系统里面的库里面的各种函数形成链接关系,所以就出现了链接
gcc -o code code.o 生成可执行文件
链接
这里没有手工链接库,是因为gcc能自己去找库,只要给它.o文件,它就能自己去链
查看链接库
使用下面这个命令,可查看你链接了哪个库
ldd code
在Linux库中
动态库通常以 .so 结尾,静态库通常以 .a 结尾
在windows库中
动态库通常以 .dll 结尾,静态库通常以 .lib 结尾
判别库是什么库,一般去掉lib三个字符就知道它是什么库了,去掉其它后缀
[lhl@VM-20-2-centos LinuxStudy]$ ll /lib/libc.so.6
lrwxrwxrwx 1 root root 12 Mar 23 2023 /lib/libc.so.6 -> libc-2.17.so
libc中,把lib去掉就是c,c就是c库
库一般都是提前下好了
库的分类
我们链接的时候,用的有两种库,一种是动态库,一种就是静态库,分别对应动态链接和静态链接
动静态库本质都是文件,头文件都是文件
动态库
动态库是在动态链接中,程序在运行时才去找加载所需的库代码的地方。
- 被多个使用者共享使用,一旦缺失,所有链接了动态库的程序都运行不了
使用动态库的优点是:比较节省资源,不会出现太多重复代码
比如printf()函数在库里面,我在编写的时候用到了这个程序用了printf(),那个程序又用来printf(),但是我在运行的时候我只需要链接即可,不用搞很多代码。
这里所指的资源,不单指磁盘空间资源,还有网络资源和内存资源。
缺点:
对库的依赖性很强,一旦库丢失,整体所有使用这个库的程序都将无法运行
静态库
静态库在一个程序编译运行的时候,它会把库当中的方法,凡是程序运行需要用的,拷贝一份,放到程序里去,就是静态链接
默认情况下,我们都是没有安装静态库的
安装静态库的指令是
sudo yum install -y glibc-static libstdc++-static
使用静态库的优点是:
不依赖于库,我的可执行程序还可以在同类型平台中都可以运行使用
使用静态库的缺点是:
可执行程序体积比较大,比较浪费资源
验证
创建一个test.c文件
#include<stdio.h>
int main(){
printf("hello\n");
return 0;
}
ldd可以查看一个文件所链接的情况
[lhl@VM-20-2-centos lesson8]$ ldd mybin
linux-vdso.so.1 => (0x00007ffd10cc2000)
libc.so.6 => /lib/libc.so.6 (0x00007fa28068b000)
/lib/ld-linux-x86-.so.2 (0x00007fa280a59000)
动态库也叫共享库
[lhl@VM-20-2-centos lesson8]$ file mybin
mybin: ELF -bit LSB executable, x86-, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=19c375fef56bbbb83bef3c716666dd7214699481, not stripped
此刻说明这个可执行程序是动态链接的
那么如何将动态链接转为静态链接呢?
给gcc命令加上 -static
gcc test.c -o mybin-static -static
[lhl@VM-20-2-centos lesson8]$ ldd mybin-static
not a dynamic executable
[lhl@VM-20-2-centos lesson8]$ file mybin-static
mybin-static: ELF -bit LSB executable, x86-, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=5a930ed909be002bfc59e084b3ddf2e79b5204, not stripped
我们可以发现mybin(采用动态链接) 和 mybin-static (采用静态链接)它们两的体积
8360 和 861288
这还仅仅只是调用了一个printf函数!
动态链接是gcc的默认链接方式!!!
创建一个cpp的文件
1 #include<iostream>
2 using namespace std;
3
4 int main(){
5 cout<<"hello libc++"<<endl;
6 return 0;
7 }
ldd mybincpp
[lhl@VM-20-2-centos lesson8]$ ldd mybincpp
linux-vdso.so.1 => (0x00007fff4e521000)
libstdc++.so.6 => /home/lhl/.VimForCpp/vim/bundle/YCM.so/el7.x86_/libstdc++.so.6(0x00007fe28a43e000)
libm.so.6 => /lib/libm.so.6 (0x00007fe28a13c000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007fe2f26000)
libc.so.6 => /lib/libc.so.6 (0x00007fe2b58000)
/lib/ld-linux-x86-.so.2 (0x00007fe28a7bf000)
[lhl@VM-20-2-centos lesson8]$ file mybincpp
mybincpp: ELF -bit LSB executable, x86-, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=82f32a349672d07423feb750d6276e3a1f2bc480, not stripped
[lhl@VM-20-2-centos lesson8]$ ldd testcpp-static
not a dynamic executable
[lhl@VM-20-2-centos lesson8]$ file testcpp-static
testcpp-static: ELF -bit LSB executable, x86-, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=7ecdfafe6ef5572734390f282699a5d969e9, not stripped
自动化构造工具——make和makefile
make是一个命令,makefile是一个文件
makefile是我们需要自己去创建的,而且makefile里面保存的是依赖关系和依赖方法
还是以test.c为例
mybin:test.c 这个是依赖关系
gcc test.c -o mybin 这个是依赖方法
以后要编译的时候,直接make一下,它就能自动编译了,前提是你的Makefile要存在
依赖关系和依赖方法
依赖关系解决的是告诉源文件说我要依赖于你,你要编译形成可执行文件
依赖方法解决的是怎么变易形成可执行文件的问题
上述的mybin : mytest.c
冒号左侧:一般为称为目标文件
冒号右侧:一般称为依赖文件列表,依赖文件可以是有多个的
依赖方法要以tab作为开头,不是空格,不是四个空格,而是tab,这是语法要求
源代码没变的话,再次编译,编译器就会觉得没必要了
自动化清理
[lhl@VM-20-2-centos day723]$ make
gcc test.c -o mybin 第一次可以完成编译
[lhl@VM-20-2-centos day723]$ make
make: `mybin' is up to date. 第二次编译器就觉得没必要,不给编了
[lhl@VM-20-2-centos day723]$ make clean
rm -f mybin 不给编,那我们就清理一下即可
[lhl@VM-20-2-centos day723]$ make
gcc test.c -o mybin 第三次又能编了
make和makefile在形成目标文件的时候,默认是从上往下扫描的makefile文件的,默认形成的是第一个文件,且只形成一个
就是说我的某个功能只想用make的话,那么就把它放到首位去,不要放到其它位
比如我的清理功能如果想直接用make的话,就需要
1: Makefile+ ⮀ ⮂⮂ buffers
1 clean:
2 rm -f mybin
3
4 mybin:mytest.c
5 gcc mytest.c -o mybin
6
[lhl@VM-20-2-centos exam]$ make //此刻make就是删除操作
rm -f mybin
[lhl@VM-20-2-centos exam]$ make clean //原来的功能也不会改变
rm -f mybin
[lhl@VM-20-2-centos exam]$ make mybin //此刻的编译就要写清楚了
gcc mytest.c -o mybin
[lhl@VM-20-2-centos exam]$ make //用make是直接删除了
rm -f mybin
makefile的更新机制
makefile是怎么知道我的可执行文件已经是最新版本的呢
通过对比时间,看可执行文件的修改时间是否比源文件的修改时间新就可以了
时间查看语句
stat file_name
[lhl@VM-20-2-centos exam]$ stat mytest.c
File: ‘mytest.c’
Size: 75 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/769d Inode: 1579442 Links: 1
Access: (06/-rw-rw-r--) Uid: ( 1002/ lhl) Gid: ( 1002/ lhl)
Access:
2024-07-23 18:21:19.048779699 +0800
Modify: 2024-07-23 18:21:19.045779717 +0800
Change: 2024-07-23 18:21:19.045779717 +0800
Birth: -
[lhl@VM-20-2
modify 与 change 时间区别
modify time指的是文件内容的修改
change time指的是文件属性的修改
因为文件 = 内容 + 属性
验证(modify & change)
对内容修改
对属性修改
为什么modify time 的修改会带动change time的修改,而反过来却不会呢
因为在对文件的增删改的时候,文件的大小会变,而文件大小作为文件的属性,大小改变,属性肯定也会跟着改变,所以change time自然也会改变
access time :最近访问时间
因为访问文件,查看文件,比其它修改文件内容和属性等其它操作更为频繁,每次访问要修改时间并且写入磁盘,极有可能造成Linux本身低效问题。所以每访问一次时,不会就立刻更新,而是达到一定的机制后才更新
这样操作就保证在访问上不会太多修改时间对操作系统造成负担
touch更新
所以当我们想再不修改文件内容,达到重新刷新文件时,我们应该怎么做
touch file_name
touch 在有目标文件时,它是更新,没有目标文件时,它是创建
touch一下,又可以了
伪目标
那即便有了touch文件之后,但我还是觉得很麻烦很麻烦
就是不想每次都去更新它,但是有想重新编译,那又该如何呢?
但是一般不建议将源文件跟可执行文件这对依赖关系变成伪目标
因为当我们系统里存在相当多的源文件的时候,带来的直接结果就是,当我们在编译的时候,如果我们的源文件代码有万行,百万行,千万行,我们出问题的时候,老是会不辨新旧的重新编译它,那么此刻的效率会变得多么低下
这样倒还好
makefile特殊字符标记
在我们写目标文件和源文件的时候,我们可以不用特意写出来了,用$@ 和 $^
$@代表目标文件
$^代表的是依赖文件列表
当然gcc 与 -o也可以被替换掉,甚至还可以这样
依赖关系的推导过程
我们现实中是不会这么写的
以上便是此次博文的学习内容,如有错误,还望各位大佬指点出来,谢谢阅读