1.g++/gcc的使用
在学习gcc/g++之前,需要先回顾一下程序的翻译过程:
预处理(头文件展开、去注释、宏替换、条件编译)
编译:把c编程汇编语言
汇编:把汇编变成二进制(不是可执行,二进制目标文件)
链接:把写的代码和c标准库中的代码合起来
gcc的格式:gcc [选项] 要编译的文件 [选项] [目标文件]
[wjmhlh@VM-12-9-centos lesson7]$ gcc test.c
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 16
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 9 20:27 a.out
-rw-rw-r-- 1 wjmhlh wjmhlh 320 Nov 9 20:27 test.c
[wjmhlh@VM-12-9-centos lesson7]$ ./a.out
hello1
hello2
hello3
直接使用语句:gcc test.c 会直接编译完成,不会显示程序的翻译的过程。
因此,以下代码的含义:
预处理:
gcc -E test.c -o test.i-o:指明形成的临时文件名称,.i(后缀,在linux中后缀没意义,是给人看的) ;-E:从现在开始,进行程序的翻译,当我将预处理做完,就停下来。
[wjmhlh@VM-12-9-centos lesson7]$ gcc -E test.c -o test.i
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 36
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 9 20:27 a.out
-rw-rw-r-- 1 wjmhlh wjmhlh 320 Nov 9 20:27 test.c
-rw-rw-r-- 1 wjmhlh wjmhlh 17005 Nov 9 20:30 test.i
可以使用vim test.i 打开查看:可以看到,头文件展开后的预处理的代码。并且,注释部分没有了,在条件编译下的printf("hello show\n");也消失了,因为SHOW这个宏没定义,所以只保留了default那一行代码,这就是条件编译;
[wjmhlh@VM-12-9-centos lesson7]$ ./a.out
hello1
hello2
hello3
hello deafult
宏: 234
如果需要条件编译生效,需要定义宏
以下是结果:
hello1
hello2
hello3
hell show
宏: 234
同时:我们也可以在外面定义宏:在文件名后加-D+宏名
[wjmhlh@VM-12-9-centos lesson7]$ gcc test.c -DSHOW
[wjmhlh@VM-12-9-centos lesson7]$ ./a.out
hello1
hello2
hello3
hell show
宏: 234
[wjmhlh@VM-12-9-centos lesson7]$ gcc test.c
[wjmhlh@VM-12-9-centos lesson7]$ ./a.out
hello1
hello2
hello3
hello deafult
宏: 234
当预处理完后,代码还是C语言,只不过是“干净”的C语言,没有注释没有未定义。
于是,接下来,要将C语言变成汇编语言。
编译
gcc -S test.c -o test.s -S:从现在开始,进行程序的翻译,做完编译工作,变成汇编之后,就停下来
[wjmhlh@VM-12-9-centos lesson7]$ gcc -S test.c -o test.o
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 40
-rw-rw-r-- 1 wjmhlh wjmhlh 321 Nov 9 20:47 test.c
-rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov 9 20:45 test.i
-rw-rw-r-- 1 wjmhlh wjmhlh 710 Nov 9 20:55 test.s
[wjmhlh@VM-12-9-centos lesson7]$ vim test.s
下面这个图的test.o,写错了,应该是test.s
此时,已经将C语言,变成汇编语言了,但是,汇编语言并不能被计算机执行,而是二进制;
于是,需要进行汇编,汇编就是将汇编语言,变成二进制(不可执行,二进制目标文件)。
汇编
gcc -c test.s -o test.o -c:从现在开始,进行程序的翻译,做完汇编工作,编程可重定向目标二进制,就停下来
[wjmhlh@VM-12-9-centos lesson7]$ gcc -c test.c -o test.o
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 44
-rw-rw-r-- 1 wjmhlh wjmhlh 321 Nov 9 20:47 test.c
-rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov 9 20:45 test.i
-rw-rw-r-- 1 wjmhlh wjmhlh 1808 Nov 9 21:04 test.o
-rw-rw-r-- 1 wjmhlh wjmhlh 710 Nov 9 21:01 test.s
[wjmhlh@VM-12-9-centos lesson7]$ vim test.o
虽然变成了二进制,但是还是不能被执行,因为少了链接,没有将库结合起来。因为这三部,都只在编译我的代码,并没有带上C标准库上的代码。
链接
gcc test.o -o mytest 链接过程,形成了可执行程序,可执行的二进制程序(库+我写码)。
[wjmhlh@VM-12-9-centos lesson7]$ gcc test.o
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 44
-rwxrwxr-x 1 wjmhlh wjmhlh 8408 Nov 9 21:08 a.out
-rw-rw-r-- 1 wjmhlh wjmhlh 321 Nov 9 20:47 test.c
-rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov 9 20:45 test.i
-rw-rw-r-- 1 wjmhlh wjmhlh 1809 Nov 9 21:04 test.o
-rw-rw-r-- 1 wjmhlh wjmhlh 710 Nov 9 21:01 test.s
当然,如果我们想重命名,可以带上-o
[wjmhlh@VM-12-9-centos lesson7]$ gcc test.o -o mytest
[wjmhlh@VM-12-9-centos lesson7]$ ll
total 56
-rwxrwxr-x 1 wjmhlh wjmhlh 8408 Nov 9 21:08 a.out
-rwxrwxr-x 1 wjmhlh wjmhlh 8408 Nov 9 21:10 mytest
-rw-rw-r-- 1 wjmhlh wjmhlh 321 Nov 9 20:47 test.c
-rw-rw-r-- 1 wjmhlh wjmhlh 16988 Nov 9 20:45 test.i
-rw-rw-r-- 1 wjmhlh wjmhlh 1809 Nov 9 21:04 test.o
-rw-rw-r-- 1 wjmhlh wjmhlh 710 Nov 9 21:01 test.s
[wjmhlh@VM-12-9-centos lesson7]$ ./mytest
hello1
hello2
hello3
hello deafult
宏: 234
在链接这一步:
在这里涉及到一个重要的点:函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
接下来,就是函数库的作用了!
函数库一般分为静态库和动态库
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。
静态链接:好处:不受库升级或被删除的影响。坏处:形成的可执行程序太大。
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销,即形成的可执行程序小。
动态链接:好处:形成的可执行程序小。坏处:受库的改动影响。
那么,在一般情况下,我们的Linux的函数库是静态还是动态的呢?
先看下面代码:
[wjmhlh@VM-12-9-centos lesson8]$ touch mytest.c
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 0
-rw-rw-r-- 1 wjmhlh wjmhlh 0 Nov 10 15:07 mytest.c
[wjmhlh@VM-12-9-centos lesson8]$ vim mytest.c
[wjmhlh@VM-12-9-centos lesson8]$ gcc mytest.c -o mytest
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 16
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 15:09 mytest
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
从上面看到,我们形成的可执行程序的大小是8360,也就是大概8k左右。
于是,我们继续看:
[wjmhlh@VM-12-9-centos lesson8]$ file mytest
mytest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ea8c82beaf490b03226c3200ce7a0cbb54cd0bc2, not stripped
[wjmhlh@VM-12-9-centos lesson8]$ ldd mytest
linux-vdso.so.1 => (0x00007ffd7e35d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f88f1346000)
/lib64/ld-linux-x86-64.so.2 (0x00007f88f1714000)
在解释上面的代码前,先要了解一下在Linux下库的命名:
动态库:libXXXXXX.so 以lib为前缀,以.so为后缀,可以带上版本号,XXX是库的名字
静态的:libYYYYYY.a 以lib为前缀,以.a为后缀,可以带上版本号,YYY是库的名字
而要区分是什么库,那么,去掉前缀和后缀,剩下的就是我们需要知道的库。
如上面:libc.so.6——>lib c .so.6 最终我们看到的是我们最熟悉的c,也就是c的标准库了,是个动态库,这也说明了,在Linux下,默认的是动态库。
这也是我们写的头文件中的stdio中的std的意思——C标准库,用了标准库。
这时候就会有个疑问?既然我们在stdio.h中使用了C标准库,那么C标准库在哪?咋看不见?
其实就是在lib64/libc.so.6 这里。
[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc.so.6
/lib64/libc.so.6
[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc.so.6 -l
lrwxrwxrwx 1 root root 12 Jul 25 16:58 /lib64/libc.so.6 -> libc-2.17.so
于是,我们可以查看我们的库了。
[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc-2.17.so -al
-rwxr-xr-x 1 root root 2156592 May 19 00:18 /lib64/libc-2.17.so
其中,2156592就是库的大小
如果我们需要静态库了,那咋办?
我们可以在gcc的时候,在后面加上-static。
[wjmhlh@VM-12-9-centos lesson8]$ gcc mytest.c -o mytest_s -static
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 860
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 15:09 mytest
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
-rwxrwxr-x 1 wjmhlh wjmhlh 861288 Nov 10 15:26 mytest_s
我们来查看一下它所使用的库:
[wjmhlh@VM-12-9-centos lesson8]$ file mytest_s
mytest_s: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=b9f8bdddd842b61ac994be5e91eff1d7e3e64234, not stripped
[wjmhlh@VM-12-9-centos lesson8]$ ldd mytest_s
not a dynamic executable-------不是可动态执行的
可以看到,确实是静态库了,使用了静态链接,同时,我们可以看到静态链接和动态链接的体积区别
静态:861288
动态:8360
一百倍!!!😀要是我需要下载的软件,一家公司是8360大小,另外一家是861288大小,功能等等几乎一样,我肯定选8360啊!!!
其实,在这里我们就能继续看到,我们在Linux的指令,其实都是动态库中的。
[wjmhlh@VM-12-9-centos lesson8]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c8ada1f7095f6b2bb7ddc848e088c2d615c3743e, stripped
[wjmhlh@VM-12-9-centos lesson8]$ file /usr/bin/pwd
/usr/bin/pwd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=8f1d0ff9fee13b5d44a1189122856912af0486bc, stripped
所有,千万不要删掉系统自带的动态库!!!而且,这个库只有一份,意味着所有用C语言写的程序,不会出现重复的库代码,因此动态库也叫共享库。因此,要是去下载一个C写的程序,也不需要下载C标准库了。
而静态库呢?在执行静态链接的时候,拷贝的是.so内部的代码吗?
NO!系统里面必须存在.a结尾的静态库。
[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc.a
/lib64/libc.a
[wjmhlh@VM-12-9-centos lesson8]$ ll /lib64/libc.a
-rw-r--r-- 1 root root 5105516 May 19 00:18 /lib64/libc.a
这个就是C语言的静态库了。在静态链接的时候,会将静态库的代码拷贝过去。一般来说,系统会自带动态库,而静态库需要自己安装。
同样的对于c++,一样有自己的C++标准库,动静态库。
[wjmhlh@VM-12-9-centos lesson8]$ touch mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 4
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
-rw-rw-r-- 1 wjmhlh wjmhlh 0 Nov 10 15:42 mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ vim mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 8
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
-rw-rw-r-- 1 wjmhlh wjmhlh 188 Nov 10 15:43 mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ g++ mytest.cpp -o mytestcpp
[wjmhlh@VM-12-9-centos lesson8]$ ldd mytestcpp
linux-vdso.so.1 => (0x00007ffff6c62000)
libstdc++.so.6 => /home/wjmhlh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6 (0x00007f42d0d10000)
libm.so.6 => /lib64/libm.so.6 (0x00007f42d0a0e000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f42d07f8000)
libc.so.6 => /lib64/libc.so.6 (0x00007f42d042a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f42d1091000)
wjmhlh@VM-12-9-centos lesson8]$ g++ mytest.cpp -o mytestcpp -static
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 1580
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
-rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp
-rw-rw-r-- 1 wjmhlh wjmhlh 188 Nov 10 15:43 mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ ldd mytestcpp
not a dynamic executable
[wjmhlh@VM-12-9-centos lesson8]$ ll
total 1580
-rw-rw-r-- 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c
-rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp
-rw-rw-r-- 1 wjmhlh wjmhlh 188 Nov 10 15:43 mytest.cpp
[wjmhlh@VM-12-9-centos lesson8]$ file mytestcpp
mytestcpp: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=58b7771454ee8e14960bf100fb6376db5d8a9a03, not stripped
最后:系统本身为了支持我们去编程,给我们提供了标准库的.h(接口),标准的动静态库.so/.a(实现方法)。所以,我们的代码+库代码==可执行程序!
其实,上述的内容,在windows下的原理差不多,也需要动静态库。windows的动态库的后缀:.dll,静态库:.lib。
2.Makefile
makefile是什么?
makefile是一个工具,可以"自动化编译",只需要一个make命令,整个工程就会完全自动编译,大大地提高软件开发效率。
makefile是一个文件,make是一个命令。
如何使用makefile?注:可以大写,可以小写。
首先:创建makefile文件,文件名必须的是makefile
[wjmhlh@VM-12-9-centos mk]$ touch makefile
[wjmhlh@VM-12-9-centos mk]$ ll
total 4
-rw-rw-r-- 1 wjmhlh wjmhlh 0 Nov 10 23:40 makefile
然后:vim makefile
要往里面写入依赖关系和依赖方法:
保存退出。之后,我们不用再使用gcc或g++了,直接使用make命令,完成自动编译
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ cat makefile
mycode:mycode.c
gcc mycode.c -o mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 42 Nov 10 23:44 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 23:43 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ ./mycode
hello makefile
如果这样子来看,其实反应不出来makefile的作用如何之大。因为工程量少。等工程量大,就知道了。因此现在所知道的作用,便是再makfile里面写gcc,等以后随便写代码后,直接make,不需要gcc,方便了!
makefile原理:
mycode:mycode.c
gcc mycode.c -o mycode根据上面解释的依赖方法和依赖关系:mycode是要形成的可执行程序,mycode是mycode.c的儿子,因此mycode.c的mycode的依赖对象,而形成的(依赖)方法便是 gcc mycode.c -o mycode 。
目的:依赖对象
依赖方法
↑ 这里的空格,必须以table为开头!必须以table为开头!必须以table为开头!
因为makefile是文件,因此我们需要知道的是,makefile内部,要写的就是依赖方法和依赖对象!
注意:是文件,不是目录(文件夹)。创建的时候,是touch makefile,不是mkdir makefile。
[wjmhlh@VM-12-9-centos mk]$ touch makefile
[wjmhlh@VM-12-9-centos mk]$ vim makefile
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 20
-rw-rw-r-- 1 wjmhlh wjmhlh 40 Nov 11 10:03 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ ./mycode
hello makefile
如果有一天,我不想要这个可执行程序了,那么改如何清理呢?
我们可以再下面写上clean:
从此:想要编译:make 想要清理已经编译好的可执行程序:make clean
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ make clean
rm -f mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 8
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
具体解释:
①clean不依赖任何对象,是独立存在的。因此直接换行写下一行。
clean:
rm -f mycode
rm -f mycode 是clean的依赖方法。
而为了表明clean是清理这里的项目,那么加上.PHONY:clean,来代表伪目标;
伪目标是什么?
.PHONY:被该关键字修饰的对象,是一个伪目标
首先来了解一下一些细节:
当我们make一个可执行程序后,如果继续make,它就会显示,已经是最新的可执行程序,不需要编译了。
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ make
make: `mycode' is up to date.
[wjmhlh@VM-12-9-centos mk]$ make
make: `mycode' is up to date.
[wjmhlh@VM-12-9-centos mk]$ make
make: `mycode' is up to date.
如果我们在这种情况下,很久没用这个代码,而这个代码没有任何修改,不需要再make。
如果我们在这种情况下,去改写代码,那么我们还需要再make一下吗?需要。
换句话说:同样的代码,只需要make一次。
但是我们将同样的思路去这些make clean,却发现,make clean可以一直被执行,即使可执行程序已经被清理了,但还是可以执行make clean。
[wjmhlh@VM-12-9-centos mk]$ make clean
rm -f mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 8
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ make clean
rm -f mycode
[wjmhlh@VM-12-9-centos mk]$ make clean
rm -f mycode
[wjmhlh@VM-12-9-centos mk]$ make clean
rm -f mycode
为什么会这样?
其实,这就是.PHONY 的作用:伪目标代表的含义就是:该目标,总是被执行的!
我们试一下,用.PHONY来修饰mycode(目标)
-rw-rw-r-- 1 wjmhlh wjmhlh 91 Nov 11 10:23 makefile
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 20
-rw-rw-r-- 1 wjmhlh wjmhlh 91 Nov 11 10:23 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 20
-rw-rw-r-- 1 wjmhlh wjmhlh 91 Nov 11 10:23 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
mycode变成了总是能够被执行的目标了。
我们一般不会将我们所需要的可执行程序用.PHONY来修饰,因为只要代码没修改过,只需要make一次就可以了。
其实我特别好奇:gcc是如何得知我不需要再编译了呢?
这就需要提到三个时间了
[wjmhlh@VM-12-9-centos mk]$ stat mycode.c
File: ‘mycode.c’
Size: 81 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 789551 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ wjmhlh) Gid: ( 1001/ wjmhlh)
Access: 2022-11-10 23:39:57.210243832 +0800
Modify: 2022-11-10 23:39:55.912237391 +0800
Change: 2022-11-10 23:39:55.912237391 +0800
[wjmhlh@VM-12-9-centos mk]$ stat mycode
File: ‘mycode’
Size: 8360 Blocks: 24 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 789553 Links: 1
Access: (0775/-rwxrwxr-x) Uid: ( 1001/ wjmhlh) Gid: ( 1001/ wjmhlh)
Access: 2022-11-11 10:24:14.205381640 +0800
Modify: 2022-11-11 10:24:13.801379696 +0800
Change: 2022-11-11 10:24:13.801379696 +0800
①Modify:指文件内容被修改的时间
②Change:指文件属性被修改的时间
③Access:指文件被访问后的时间
例如:我们去改文件属性:
当前的mycode的Change:2022-11-11 10:24:13.801379696 +0800
其他两个时间:
Access: 2022-11-11 10:24:14.205381640 +0800
Modify: 2022-11-11 10:24:13.801379696 +0800
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:28 makefile
-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ chmod u-w mycode
[wjmhlh@VM-12-9-centos mk]$ ll
total 20
-rw-rw-r-- 1 wjmhlh wjmhlh 77 Nov 11 10:28 makefile
-r-xrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode
-rw-rw-r-- 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c
[wjmhlh@VM-12-9-centos mk]$ stat mycode
File: ‘mycode’
Size: 8360 Blocks: 24 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 789553 Links: 1
Access: (0575/-r-xrwxr-x) Uid: ( 1001/ wjmhlh) Gid: ( 1001/ wjmhlh)
Access: 2022-11-11 10:24:14.205381640 +0800
Modify: 2022-11-11 10:24:13.801379696 +0800
Change: 2022-11-11 10:32:35.103783432 +0800
此时的时间:
Access: 2022-11-11 10:24:14.205381640 +0800
Modify: 2022-11-11 10:24:13.801379696 +0800
Change: 2022-11-11 10:32:35.103783432 +0800
可以得知:这里的Change的时间被改变了,其他两个没有发生改变
再看对文件内容的修改:
[wjmhlh@VM-12-9-centos mk]$ vim mycode.c
[wjmhlh@VM-12-9-centos mk]$ make
gcc mycode.c -o mycode
[wjmhlh@VM-12-9-centos mk]$ stat mycode
File: ‘mycode’
Size: 8360 Blocks: 24 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 789553 Links: 1
Access: (0775/-rwxrwxr-x) Uid: ( 1001/ wjmhlh) Gid: ( 1001/ wjmhlh)
Access: 2022-11-11 10:36:36.216934574 +0800
Modify: 2022-11-11 10:36:35.814932656 +0800
Change: 2022-11-11 10:36:35.814932656 +0800
同理,这里会对Modify的时间做修改,但是我们可以看见的是,Change的时间也被修改了。
这是为什么?
因为对内容改变了,文件的大小也改变了,即属性也改变了。
这里很合理,但不可思议的是,Access为啥不变(我举的这个例子,因为我make了一下,时间间隔比较长,所以改变了,其实是没变的。)
Access代表是,访问这个文件的时间。
其实访问它,确实是会变,但是呢,如果在短时间内(非常短的时间),它不会变。即不会频繁去改变时间。其原因是,我们会频繁地访问文件,如果频繁地去修改时间,意味着要非常多次地去访问磁盘,是一个负担!而且,很少人去关心文件被访问的时间,而且去关心文件的内容属性被修改的时间。
回到那个问题:gcc如何得知不需要再编译可执行程序?
首先:我们需要知道的是,一定是现有源文件,再有可执行程序。所以!在编译完成,生产可执行程序后,源文件的Modify时间一定比可执行程序的Modify时间早!
于是,我们就知道了,gcc是通过比较两个时间来得知是否需要重新编译。
如果源文件的Modify比可执行程序的Modify早,说明,代码没有被修改,不需要make重新编译。
如果源文件的Modify比可执行程序的Modify晚,说明代码被修改,需要make重新编译。
此时,我们可以去想想,为什么加了.PHONHY后,总是被执行,.PHONY是怎么做的?
其实原理就是跟touch一下已有的源文件(可以理解为摸一下文件,导致文件时间改变了)一样。所以加了.PHONY的作用就是,不要再去对比时间了,直接给我make!
再来看看新的问题:为什么直接输入make,会自己识别是编译可执行程序?而不是清理。清理则需要输入make clean?
编译可执行程序的时候,我们其实也可以写成make mycode。只不过呢?make这个命令,是从上到下,按顺序执行。
也就是说,第一个被make碰到的目标文件,可以省略掉目标文件名,仅此而已。
但这里也有个问题?不是说好make是按顺序往下走吗?怎么make了后,只执行了一次?而没有继续往下走了呢?
默认情况下:makefile只形成一个目标文件,那么后续的,需要再此输入命令。
makefile的推到规则:
其实在写makefile时,mycode依赖的不是mycode.c,而是mycode.o;
而mycode.o依赖的是mycode.s;
mycode.s依赖的是mycode.i;
mycode.i依赖的是mycode.c
在执行make命令的时候,从上到下扫描,要生成mycode,找mycode.o,没有mycode.o,那就找往下找,一直找。