Linux基于CentOS7【gcc/g++】【gcc下的编译原理】【makefile & make】【动静态库】

gcc/g++
一般写好的代码就是用gcc来去编译

形成指定名称的可执行语句

我先touch出一个code.c文件

 touch code.c

往文件里写入代码

可开始修改为自己想要的名称        

 gcc 源文件 -o 想要的名称        还可以是         gcc -o 想要的名称 源文件

 gcc code.c -o mycode1

编译它,照样能运行

在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

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 /lib64/libc.so.6 
lrwxrwxrwx 1 root root 12 Mar 23  2023 /lib64/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 => /lib64/libc.so.6 (0x00007fa28068b000)
            /lib64/ld-linux-x86-64.so.2 (0x00007fa280a59000)

动态库也叫共享库

[lhl@VM-20-2-centos lesson8]$ file mybin

 
mybin: ELF 64-bit LSB executable, x86-64, 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 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=5a96430ed909be002bfc59e084b3ddf2e79b5204, not stripped
 

我们可以发现mybin(采用动态链接) 和 mybin-static (采用静态链接)它们两的体积

8360 和 861288 

这还仅仅只是调用了一个printf函数!

动态链接是gcc的默认链接方式!!!

创建一个cpp的文件

1 #include<iostream>
2 using namespace std;

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_64/libstdc++.so.6(0x00007fe28a43e000)                                                                                                                
            libm.so.6 => /lib64/libm.so.6 (0x00007fe28a13c000)
            libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe289f26000)
            libc.so.6 => /lib64/libc.so.6 (0x00007fe289b58000)
            /lib64/ld-linux-x86-64.so.2 (0x00007fe28a7bf000)

 [lhl@VM-20-2-centos lesson8]$  file mybincpp 


mybincpp: ELF 64-bit LSB executable, x86-64, 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 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=7ecdfafe6ef895572734390f28269989a5d969e9, 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/64769d    Inode: 1579442     Links: 1
Access: (0664/-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也可以被替换掉,甚至还可以这样

依赖关系的推导过程

我们现实中是不会这么写的 

以上便是此次博文的学习内容,如有错误,还望各位大佬指点出来,谢谢阅读
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/2301_76219154/article/details/140583326

  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值