一、补充与复习
Linux在运行可执行程序的时候,有两种运行方式:
./mytest
(表示当前路径下的可执行程序 - 用/分隔开)
/home/shy/108/lesson8/mytest
(也可以运行程序,但是是在绝对路径下!)
在Linux系统中执行任意一个程序,必须在系统中找到这个程序对应的二进制文件。
(指令不需要加路径就能找到是因为系统的默认设置! --- 环境变量)
二、make / makefile
1、创建的makefile文件首字母不区分大小写!
2、依赖方法不是4个空格!是tab键!
3、makefile使用#注释
实例1:尝试写一个makefile文件
第一行为依赖关系;
第二行为依赖方法;
1 mytest:mytest.c
2 gcc -o mytest mytest.c
3 clean:
4 rm -f mytest
通过make来编译;通过make clean清除可执行程序;
如果我们详细的写出每一步:
1 mytest:mytest.o
2 gcc -o mytest mytest.o
3 mytest.s:mytest.i
4 gcc -S mytest.i -o mytest.s
5 mytest.o:mytest.s
6 gcc -c mytest.s -o mytest.o
7 mytest.i:mytest.c
8 gcc -E mytest.c -o mytest.i
9 clean:
10 rm -f mytest
make会自动推导makefile中的依赖关系---栈式结构(不区分顺序)
- make会在makefile中自动搜索第一个目标文件作为默认的目标文件(如果clean放在最首端,则此时执行make会默认执行clean!------ 因此建议将clean放到最后面!)
- 还可以单独执行生成一个目标文件:例如make mytest.i
可以发现,当我们执行完make之后,如果源文件没有修改,再次执行make不会重新编译!
目的:为了提高编译效率!
怎么做到的?
一定是源文件形成可执行,才有源文件;一般来说,源文件的最近修改时间比可执行老;
如果修改了源文件,历史上曾经还有可执行,那么源文件的最近修改时间,一定要比可执行程序新!
因此!只需要比较可执行程序的最近修改时间和源文件的最近修改时间即可!
- .exe新于.c源文件,说明不需要重新编译;
- .exe老于.c源文件,需要重新编译;
- 一般来说.exe 的修改时间不等于.c的修改时间
通过指令stat可以查看文件相关的时间属性:
stat mycode.c
文件分为:文件内容 + 文件属性
- Access: 文件被访问的时间(cat / vim / 增删改查等)
- Modify: 文件被修改(文件内容被修改)的时间 ()
- Change: 文件被改变(文件属性被修改)的时间 ()
一般情况下:文件内容被修改,文件的属性也会被修改!(例如增删查改内容,导致文件的大小被修改 > 文件的属性被修改 > 且在修改文件的时候打开了文件,AMC都被修改!)
但是如果只是chmod + 文件的权限 --- 没有修改内容 --- 导致只有change修改!
可以发现:有时候Modify和Change的时间发生改变,但是Access的时候不变,这是由于access一直修改的话太过于频繁,不利于整机效率的提高。
如果我们想更新整个文件的时间,可以使用下面命令:
touch mytest.c
如果一个文件没有被创建,touch则是创建一个新文件,如果该文件已经被创建,则是更新该文件的时间!
touch -a mytest.c
只更新access时间!
一般来说,比较两个文件的新旧时间,通常比较的是Modify!
make会根据源文件和目标文件的新旧,判定是否需要重新执行依赖关系进行编译!
此时只要我们重新更新一下源代码的时间,即可重新编译!
如果我们想要对应的依赖关系总是被执行?!(引入伪目标关键字.PHONY)
例如:
1 .PHONY:mytest
2 mytest:mytest.o
3 gcc -o mytest mytest.o
4 mytest.s:mytest.i
5 gcc -S mytest.i -o mytest.s
6 mytest.o:mytest.s
7 gcc -c mytest.s -o mytest.o
8 mytest.i:mytest.c
9 gcc -E mytest.c -o mytest.i
10 clean:
11 rm -f mytest mytest.i mytest.s mytest.o
加入.PHONY:mytest之后,生成的mytest不受时间约束!,可以一直执行make!
一般建议:清理指令用.PHONY修饰!
1 mytest:mytest.c
2 gcc -o mytest mytest.c
3 .PHONY:clean
4 clean:
5 rm -f mytest
引入两个自动变量:
makefile
中,$@
和 $^
是特殊的自动变量,用于代表特定的目标和依赖关系。
$@表示依赖关系左边,即需要生成的目标文件;
$^表示依赖关系右边,即需要的源文件!
如下:
1 mytest:mytest.c
2 gcc -o $@ $^
3 .PHONY:clean
4 clean:
5 rm -f mytest
可以发现:当我们执行make / make clean的时候,对应的指令会回显出来!
如果我们不想回显,可以在对应的依赖方法加入@符号!
1 mytest:mytest.c
2 @gcc -o $@ $^
3 .PHONY:clean
4 clean:
5 @rm -f mytest
三、进度条小游戏
1、回车换行
在C语言中,一个\n执行的是回车 + 换行!
但是回车不等于换行!
回车:把光标移动到当前行的最左端!
换行:从该行当前位置直接移动到下一行!
如果我们只想进行回车:\r
2、缓冲区
使用makefile编写进度条小程序:
- makefile中目前不用体现出头文件,编译只需要带.c文件即可(只要对应的.c文件中包含了头文件即可)
- 如果需要调用sleep函数:在Linux系统中需要调用#include<unistd.h>;在windows系统中需要调用#include<windows.h>
接下来我们对代码进行调试:(修改main.c的代码)
1 #include"processBar.h"
2 #include<unistd.h>
3 int main()
4 {
5 printf("hello world\n"); // 1
6 sleep(2); //2
7 return 0;
8 }
运行./processBar可以发现打印hello world后休眠了2秒!
如果我们此时再将\n去掉:
1 #include"processBar.h"
2 #include<unistd.h>
3 int main()
4 {
5 printf("hello world"); // 1
6 sleep(2); //2
7 return 0;
8 }
看到的结果变成了先休眠两秒,再打印hello world!
C语言的规则一定是先执行printf再进行sleep睡眠!(在sleep期间,"hello world"一定是被保存起来的!--- 被保存到了缓冲区!>>> 就是由C语言维护的一段内存!>>> stdout)
默认的是程序结束 / 程序退出内容才会被刷新出来!
怎么进行强制刷新?
C语言默认会打开三个流:标准输入(FILE* stdin)、标准输出(显示器,FILE* stdout)、标准错误(FILE* stderr)
fflush函数用于刷新一个流:
int fflush(FILE* stream);
因此,如果此时我们想要快速的进行刷新:fflush(stdout);
1 #include"processBar.h"
2 #include<unistd.h>
3 int main()
4 {
5
6 printf("hello world"); // 1
7 fflush(stdout);
8 sleep(2); //2
9 return 0;
10 }
接下来可以尝试写一个倒计时(覆盖当前位置的时间) :
此时我们可以借鉴 \r + 刷新流:
1 #include"processBar.h"
2 #include<unistd.h>
3 int main()
4 {
5 int cnt = 9;
6 while(cnt>=0)
7 {
8 printf("%d\r",cnt);
9 fflush(stdout);
10 cnt--;
11 sleep(1);
12 }
13 printf("\n"); // 回车的作用为使得倒计时的命令行不被覆盖掉
14 // printf("hello world"); // 1
15 // fflush(stdout);
16 // sleep(2); //2
17 return 0;
18 }
但是!如果此时我们想倒计时从10~0,将cnt改为10,会发现打印的是10 - 90 - 80.....
这是因为显示器显示的是字符!数字123实际上是字符1,字符2,字符3......
解决方法:直接显示两个位宽的字符!这样子覆盖会使得第一个位置为空!(默认为右对齐!)
1 #include"processBar.h"
2 #include<unistd.h>
3 int main()
4 {
5 int cnt = 10;
6 while(cnt>=0)
7 {
8 printf("%2d\r",cnt);
9 fflush(stdout);
10 cnt--;
11 sleep(1);
12 }
13 printf("\n"); // 回车的作用为使得倒计时的命令行不被覆盖掉
14 // printf("hello world"); // 1
15 // fflush(stdout);
16 // sleep(2); //2
17 return 0;
18 }
如果我们想要修改为左对齐,只需要加上负号!
printf("%-2d\r",cnt);