当前位置: 首页 > news >正文

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

前言

欢迎观看Linux系列文章!!第5篇主要讲述了动静态库自动化构建make/Makefile回车与换行相关的行缓冲区

目录

前言

动静态库

动态链接

静态链接

自动化构建-make/Makefile

如何使用

语法

make的执行

注释使用 #

依赖关系推导

定义变量&使用变量

@拒绝回显

$^ 和 $@

%通配符与$<

多行依赖方法

完整演示代码

回车与换行

行缓冲区

缓冲区刷新

强制刷新

与行缓冲相关的小程序

简单倒计时程序

简单进度条程序


动静态库

Linux中,动态库文件命名格式一般为 libxxxx.so,静态库文件命名格式一般为 libxxxx.a;
Windows中,动态库文件命名格式一般为 xxxx.dll,静态库文件命名格式一般为 xxxx.lib。

ldd code

可以看到可执行文件依赖于什么库。

链接的库文件实际上就是C标准库

使用file命令,可以查看可执行文件的基本信息

file code

图中可见,code可执行文件是64bit,executable可执行文件,dynamically linked动态链接形成的。

说明code可执行文件是由动态链接而来。

动态链接

动态库是共享的,一旦丢失,所有依赖这个动态库的程序都会运行出错。将动态库里的库方法地址拷到可执行文件中,通过地址来调用库方法。

静态链接

将静态库里的库方法直接拷贝到可执行程序里,会让可执行文件的大小变得很大。

优点:不依赖库
缺点:浪费资源(磁盘空间,内存,网络等)。

自动化构建-make/Makefile

make是一个命令,makefile是一个文件

如何使用

①在同级目录下,创建Makefile文件(也可以makefile,首字母不大写)。

②为Makefile添加内容,并保存退出

第一行表示的是依赖关系,
第二行表示的是依赖方法。
两者必须同时存在

③使用make命令,就会执行Makefile中的内容

从而实现快速编译。

语法

make的执行

直接执行make命令,默认形成第一个文件。如果想指定形成就带上目标文件名称

make make clean

用make编译完成后,若源代码文件没有被修改,那么它不会再进行编译,这是为了提高效率,已完成的事情,没必要再做一次。

这是通过对比源文件和可执行文件的修改时间来对比的。

我们也可以有办法查看文件的修改时间

stat test.c

关于访问时间:因为访问文件一般都很频繁,所以为了节省资源提高效率,访问时间不会每次访问都改,而是访问达到某一数量再改。

而之前说过的touch命令,可以使文件的A、M、C三个时间都更新一遍。

若用.PHONY修饰 mycode,将其变为伪目标,就可以多次编译了。

成为伪目标的依赖方法,不管执行几次,都会再执行。

注释使用 #

依赖关系推导

如图修改Makefile

make会根据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 作为目标文件的命名。

多行依赖方法

依赖方法可以支持多行。

由此,可以实现自定义的回显方式。

完整演示代码

BIN=mycode #SRC=$(wildcard *.c) SRC=$(shell ls *.c) OBJ=&(SRC:.c=.o) RM=rm -f CC=gcc $(BIN):$(OBJ) @$(CC) $^^ -o $@ @echo "编译 $^ 成 $@" %.o:%.c $(CC) -c $< @echo "编译 $^ 成 $@" .PHONY:clean clean: @$(RM) $(BIN) $(OBJ)

回车与换行

换行就是换到下一行,而回车是先把光标放回当前行的开头,再换行。两者是不一样的。

行缓冲区

缓冲区刷新

原code.c

#include<stdio.h> #include<unistd.h> #define M 5 int main() { printf("hello world\n"); printf("hello M=%d\n", M); sleep(2); return 0; }

编译运行后,打印的内容会立即显示到终端,并等待2秒后结束。

修改code.c,删去换行符。

#include<stdio.h> #include<unistd.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函数。

#include<stdio.h> #include<unistd.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没有缓冲机制,所以其实强制刷新对它无效。

与行缓冲相关的小程序

简单倒计时程序

#include<stdio.h> #include<unistd.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 #include<stdio.h> void process(); void Flushprocess(double total, double current);

process.c

#include"process.h" #include<string.h> #include<unistd.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

#include"process.h" #include<unistd.h> #include<time.h> #include<stdlib.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是两个版本的进度条程序,前者是简单版本的。

❤~~本文完结!!感谢观看!!接下来更精彩!!欢迎来我博客做客~~❤

http://www.cnnetsun.cn/news/1931824.html

相关文章:

  • 【生成式AI成本控制黄金法则】:20年架构师亲授5大降本增效实战策略
  • 讯飞虚拟人简单交互实现
  • 避坑指南:在Unity中正确配置PICO 4 Ultra的外部文件读写权限(含AndroidManifest.xml模板)
  • LeetCode 232. Implement Queue using Stacks 题解
  • 三菱FX3U与多台力士乐VFC-x610变频器通讯实现案例详解:频率设定、启停及读取功能附程序...
  • 最小二乘法的5种变体:从加权到自适应,选对方法让模型精度翻倍
  • 科研党必备:scihub-cn命令行工具5分钟搞定文献批量下载(附常见报错解决方案)
  • 三大权威MEG公开数据集深度解析与应用指南
  • 从卫星原始数据到地表真实信息:一份给Python+GDAL用户的辐射校正全流程脚本指南
  • 缓存命中率暴跌?生成式AI应用缓存策略全解析,从Token级语义缓存到动态TTL自适应调优
  • 开关电源功率因数与效率测试实战指南
  • 爆肝整理,智能体发展万字综述!!!
  • c++学习笔记——堆
  • 没做这4类边界测试就部署AI服务?:金融/医疗/客服场景下 hallucination 爆发的3小时应急响应流程
  • AI服务注册延迟超800ms?2024年最严苛生产环境实测:3种服务发现方案TPS对比(含Latency P999数据)
  • 机器学习之预测概率问题
  • 朱雀AI检测率高怎么降?嘎嘎降AI使用教程:3步降到5%以下
  • Harness Engineering 入门指南:从提示词到AI系统设计的完整跃迁
  • Java开发者面对大模型,到底在焦虑什么?
  • 毕业论文降AI免费检测全攻略:SpeedAI科研小助手实测指南
  • PyTorch实战:5种模型剪枝方法对比与避坑指南(附代码)
  • PX4+Gazebo+MAVROS仿真环境搭建保姆级教程:从环境变量到模型文件,一次搞定所有坑
  • Anthropic Mythos 模型:CTF 测试创新高,“最后一步”凸显网络攻击潜力
  • EXCEL跨工作簿高效搜索:一键定位多表数据并提取目标单元格
  • 电力系统优化调度:MATLAB代码实现机组组合问题的混合整数线性模型
  • SQL嵌套查询如何避免全表扫描_合理使用索引辅助子查询
  • 如何实现容器滚动到底部后无缝切换至页面滚动
  • STM32 Bootloader内存规划避坑指南:H7双Bank Flash与分散加载文件(.sct)详解
  • 避坑指南:在Vue2 + docx-preview中实现文本高亮搜索,你可能遇到的3个DOM陷阱与解决方案
  • hermes安装篇