Linux系统与系统编程(5)——动静态库、自动化构建make/Makefile、回车与换行相关的行缓冲区

张开发
2026/4/16 22:46:19 15 分钟阅读

分享文章

Linux系统与系统编程(5)——动静态库、自动化构建make/Makefile、回车与换行相关的行缓冲区
前言欢迎观看Linux系列文章第5篇主要讲述了动静态库、自动化构建make/Makefile、回车与换行相关的行缓冲区。目录前言动静态库动态链接静态链接自动化构建-make/Makefile如何使用语法make的执行注释使用 #依赖关系推导定义变量使用变量拒绝回显$^ 和 $%通配符与$多行依赖方法完整演示代码回车与换行行缓冲区缓冲区刷新强制刷新与行缓冲相关的小程序简单倒计时程序简单进度条程序动静态库Linux中动态库文件命名格式一般为 libxxxx.so静态库文件命名格式一般为 libxxxx.aWindows中动态库文件命名格式一般为 xxxx.dll静态库文件命名格式一般为 xxxx.lib。ldd code可以看到可执行文件依赖于什么库。链接的库文件实际上就是C标准库使用file命令可以查看可执行文件的基本信息file code图中可见code可执行文件是64bitexecutable可执行文件dynamically linked动态链接形成的。说明code可执行文件是由动态链接而来。动态链接动态库是共享的一旦丢失所有依赖这个动态库的程序都会运行出错。将动态库里的库方法地址拷到可执行文件中通过地址来调用库方法。静态链接将静态库里的库方法直接拷贝到可执行程序里会让可执行文件的大小变得很大。优点不依赖库缺点浪费资源磁盘空间内存网络等。自动化构建-make/Makefilemake是一个命令makefile是一个文件如何使用①在同级目录下创建Makefile文件也可以makefile首字母不大写。②为Makefile添加内容并保存退出第一行表示的是依赖关系第二行表示的是依赖方法。两者必须同时存在③使用make命令就会执行Makefile中的内容从而实现快速编译。语法make的执行直接执行make命令默认形成第一个文件。如果想指定形成就带上目标文件名称make make clean用make编译完成后若源代码文件没有被修改那么它不会再进行编译这是为了提高效率已完成的事情没必要再做一次。这是通过对比源文件和可执行文件的修改时间来对比的。我们也可以有办法查看文件的修改时间stat test.c关于访问时间因为访问文件一般都很频繁所以为了节省资源提高效率访问时间不会每次访问都改而是访问达到某一数量再改。而之前说过的touch命令可以使文件的A、M、C三个时间都更新一遍。若用.PHONY修饰 mycode将其变为伪目标就可以多次编译了。成为伪目标的依赖方法不管执行几次都会再执行。注释使用 #依赖关系推导如图修改Makefilemake会根据Makefile给到的依赖关系自动推导执行依赖方法由于依赖关系①的依赖文件需要②生成②的依赖文件需要③生成③的依赖文件需要④生成而④的依赖文件code.c刚好就有。于是make指令就按④③②①的顺序推导执行。底层用的是栈的结构来完成推导的无法完成的依赖方法就依次入栈直到遇到可以执行的依赖方法就依次出栈。定义变量使用变量这里我们自定义设置了变量BIN、SRC、OBJ、RM、CC通过$([变量名])替换下文的依赖关系和依赖方法。当我们使用make的时候就会自动把变量替换到指令中。以后我们只需要修改变量就可以适配不同的文件从而便于不同文件的编译了。但是这样依旧还是不够通用。还可以这样行23相当于下面的代码。SRC$(wilcard *.c)使用shell在make的时候会先调用shell的命令 这里是ls *.c然后把命令会把打印在终端的内容给到SRC这样就可以做到批量操作一堆文件了。当然OBJ也要改这里把SRC的所有内容中的 .c 换为 .o 。这样一来OBJ也能表示对应SRC的一批 .o 文件了。拒绝回显依赖方法前加一个make的时候就不会回显了。不加加$^ 和 $我们可以用$^省略表示目标文件$省略表示依赖关系列表。同下%通配符与$表示匹配内容此处表示含有 .c 的文件都作为依赖文件并把目标文件依照 .c 依赖文件列表依次生成对应目标文件并把匹配的 .c 改为 .o。通配符要搭配 $ 使用意思是把批量的匹配到的依赖文件一个一个放到该位置并生成对应的目标文件。意思是把一堆 .c 文件依次 -c 选项编译后生成一堆目标文件并把依赖文件的 .c 换为 .o 作为目标文件的命名。多行依赖方法依赖方法可以支持多行。由此可以实现自定义的回显方式。完整演示代码BINmycode #SRC$(wildcard *.c) SRC$(shell ls *.c) OBJ(SRC:.c.o) RMrm -f CCgcc $(BIN):$(OBJ) $(CC) $^^ -o $ echo 编译 $^ 成 $ %.o:%.c $(CC) -c $ echo 编译 $^ 成 $ .PHONY:clean clean: $(RM) $(BIN) $(OBJ)回车与换行换行就是换到下一行而回车是先把光标放回当前行的开头再换行。两者是不一样的。行缓冲区缓冲区刷新原code.c#includestdio.h #includeunistd.h #define M 5 int main() { printf(hello world\n); printf(hello M%d\n, M); sleep(2); return 0; }编译运行后打印的内容会立即显示到终端并等待2秒后结束。修改code.c删去换行符。#includestdio.h #includeunistd.h #define M 5 int main() { printf(hello world); printf(hello M%d, M); sleep(2); return 0; }编译运行后等待了2秒打印的内容才会显示到终端上。毫无疑问C语言程序肯定是先执行printf再执行sleep的但是为什么最终效果看上去是先sleep再printf呢就是因为缓冲区的存在printf的内容是要打印到显示器终端的所以要打印的字符会先放到缓冲区的内存中。缓冲区有对应的刷新策略。对于显示器刷新策略是行刷新就是每次换行进行依次刷新每次刷新就把缓冲区的所有内容打印到显示器上。最后在程序退出时也会刷新缓冲区释放缓冲区内存。原code.c打印的内容自带 \n 所以打印的内容进了缓冲区之后立马就刷新打印到显示器了修改后的code.c打印的内容不带 \n 打印的内容进入了缓冲区之后没有被刷新只有等到程序结束了才会把缓冲区刷新输出所有内容。强制刷新如果想要强制刷新缓冲区可以使用fflush函数。#includestdio.h #includeunistd.h #define M 5 int main() { printf(hello world); printf(hello M%d, M); fflush(stdout); sleep(2); return 0; }fflush有三个标准源参数stdin,stdout,stderr。stdin是键盘输入缓冲stdout和stderr都是显示器输出一个输出的是程序正常执行时输出的内容一个输出的是报错时的错误信息。我们可以用fflush强制刷新stdout标准源达到刷新缓冲区的目的。注意在Linux下不要 fflush(std) 不然会报错stderr没有缓冲机制所以其实强制刷新对它无效。与行缓冲相关的小程序简单倒计时程序#includestdio.h #includeunistd.h int main() { int cnt 10; while(cnt 0) { printf(%-2d\r, cnt); fflush(stdout); cnt--; sleep(1); } printf(\n); return 0; }① \r 让光标回车不换行即回到行首在下次输出时可以覆盖原内容。②因为没有是要 \n 换行所以要用fflush强制刷新才可以及时输出倒计数数字。③要格式化为%-2d即左对齐两个字符不足两个字符空格补齐。打印完10之后光标回退到开头继续打印个位数会有空格覆盖10的0字符。简单进度条程序process.h#pragma once #includestdio.h void process(); void Flushprocess(double total, double current);process.c#includeprocess.h #includestring.h #includeunistd.h #define SIZE 101 #define STYLE # void process() { int rate 0; char buffer[SIZE]; char load[4]{/, |, \\, -}; memset(buffer, , sizeof(buffer)); buffer[100] \0; while(rate 100) { printf([%s][%d\%][%c]\r, buffer, rate, load[rate % 4]); fflush(stdout); buffer[rate] STYLE; rate; usleep(50000); } printf(\n); } void Flushprocess(double total, double current) { char buffer[SIZE]; memset(buffer, , sizeof(buffer)); buffer[SIZE - 1] \0; int num (int)(current * 100 / total); double rate current * 100.0 / total; char load[4]{/, |, \\, -}; static int index 0; for(int i 0; i num; i) { buffer[i] STYLE; } printf([%-100s][%.2lf%%][%c]\r, buffer, rate, load[index % 4]); if(num 100) { printf([%-100s][%.2lf%%][*]\r, buffer, rate); } index; }main.c#includeprocess.h #includeunistd.h #includetime.h #includestdlib.h //double total 1024.0; //double speed 1.0; //函数指针类型 typedef void (*call_t)(double, double); void download(double total, call_t cb)//回调函数 { srand(time(NULL)); double current 0.0; double speed rand() % 3 1; while(current total) { cb(total, current); if(current total) break; usleep(1000); current speed; if(current total) current total; } } int main() { //process(); download(1024.0, Flushprocess); printf(\n); download(3472.0, Flushprocess); printf(\n); download(23626.0, Flushprocess); printf(\n); download(52.0, Flushprocess); printf(\n); download(52672.0, Flushprocess); printf(\n); return 0; }process和Flushprocess是两个版本的进度条程序前者是简单版本的。❤~~本文完结感谢观看接下来更精彩欢迎来我博客做客~~❤

更多文章