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

【linux篇】动静态库和自动化构建的“神之一手”:make、Makefile

库:

  • 动态库动态库(Dynamic Library)是一种在程序运行时被加载的库文件

  1. linux:libxxx.so
  2. windows:xxxx.dll
  • 静态库:静态库(Static Library)是指在程序编译链接时,把库文件的代码全部加入到可执行文件中,从而生成一个包含所有必需库代码的可执行文件

  1. linux:libxxx.a
  2. windows:xxxx.lib

小知识:

gcc默认形成的可执行程序是动态链接的

gcc -static形成的可执行程序是静态链接的

动静态库链接:

动态链接:(动态库又叫共享库,一旦缺失,所有依赖动态库的程序都会报错)

告诉可执行程序,动态库(共享库)的地址在哪

优点:形成可执行程序的体积小

缺点:一旦缺失,所有依赖动态库的程序都会报错

静态链接:

将库实现,在链接阶段拷贝到我们的可执行程序中

优点:一旦编译好,不依赖任何库

缺点:浪费空间(磁盘、内存空间和网络等)

make/Makefile是什么:

make是一个命令

Makefile是一个文件

Makefile是一个自动化构建的工具

make/Makefile的使用:

例子:

创建一个.c文件,在里面写入代码

在创建一个Makefile文件,里面写入如下代码。表示mycode文件依赖于code.c,下面就是正常的gcc编译

最后输入make指令运行mycode,就会生成一个叫mycode的可执行文件

然后运行就行

自动化构建核心思想:

依赖关系和依赖方法,形成目标文件

第一行表示:依赖关系,mycode的形成依赖于code.c

第二行表示:依赖方法,mycode的形成依赖于code.c,以gcc code.c -o mycode的方法形成

具体语法:

依赖关系:

mycode:目标文件

code.c:依赖文件。若依赖文件有多个,则叫依赖文件列表

依赖方法:

gcc code.c -o mycode

clean也是一个目标文件,不过它的依赖文件是空的,下面则是它的依赖方法

为什么但输入一个make只形成一个目标文件?(如果把“clean:”放到第一个,单输入一个make就是形成"clean"目标文件)

  • 因为make会自顶向下扫描makefile文件默认形成第一个目标文件。如果想指定形成目标文件,就得"make+目标文件名"

makefile文件中,保存了编译器链接器的参数选项,并且描述了所有源文件之间的关系。make程序会读取makefile文件中的数据,然后根据规则调用编译器,汇编器,链接器产生最后的输出

  • Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释, B选项是正确的
  • 显式规则说明了,如何生成一个或多个目标文件。

  • make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写makefile,比如源文件与目标文件之间的时间关系判断之类

  • 在makefile中可以定义变量,当makefile被执行时,其中的变量都会被扩展到相应的引用位置上,通常使用 $(var) 表示引用变量

  • 文件指示。包含在一个makefile中引用另一个makefile,类似C语言中的include; 根据这一项可以推导Makefile可以使用include关键字把别的Makefile包含进来。

  • 注释,makefile中可以使用 # 在行首表示行注释

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件, 找到了解释这个文件

 .PHONY是什么?

  • 被.PHONY修饰的clean我们称为伪目标(什么是伪目标,如其名,就是假的目标)

  • 目标文件被“.PHONY”修饰后,它所依赖的方法则变为:总是被执行(也就是可多次执行)

  1. 它所依赖的方法则变为:总是被执行(也就是可多次执行)是怎么做到的?

  2. 对于mycode来说,就是忽略对比时间,它的依赖方法总是被执行

当make执行一次生成mycode目标文件后,为什么不能再执行?又是怎么做到的?

  • 因为mycode的依赖文件没有被修改,一模一样的文件,便不执行了。这样做是为了提高编译效率(在大型项目中,代码都是以万行打底的,运行一次项目需要几分钟几十分钟,如果不小心再次执行了一次make,那又需要生成一次目标文件,又要花上那么长时间,这是得不偿失的)

  • 对比源文件与可执行文件的M时间。因为源文件的创建肯定先于可执行文件,在执行make命令时,会判断源文件的修改时间是否在可执行文件之前。若是,则允许执行make命令,否在不给执行

  • 那源文件与可执行文件修改时间上是怎么定的?
  1. 根据文件的“ACM”时间决定

  2. Access:文件的最新访问时间(不是实时更新)

  3. Modify:文件内容的修改时间(一般修改文件内容,文件属性也会跟着改变,因为修改文件内容时,文件的大小会出现变化,此时文件的属性也就出现了辩护)

  4. Change:文件属性的修改时间(修改属性chmod命令)

要更新一个以存在的文件的ACM时间

touch+文件名

  • makefile文件注释用"#"

makefile的推导原则:

mycode文件依赖的其实是code.o文件。

  • make会进行依赖关系推导,直到依赖文件是存在的
  1. myproc依赖的是myproc.o文件,但myproc.o文件不存在,将依赖方法入栈

  2. myproc.o依赖的是myproc.s文件,但myproc.s文件不存在,将依赖方法入栈

  3. myproc.s依赖的是myproc.i文件,但myproc.i文件不存在,将依赖方法入栈

  4. myproc.i依赖的是myproc.c文件,myproc.c文件存在,将依赖方法入栈

  5. 入栈后,出栈,执行依赖方法,生成最终的目标文件

在执行依赖方法时,生成的文件可以通过clean删除

扩展语法:

类似定义变量:

BIN赋值为mytest(目标文件)

SRC动态的获取所有源文件

OBJ赋值为依赖文件

CC赋值为gcc

RM赋值为rm -f

@echo:不让echo回显

类似变量定义:

BIN赋值为mytest(目标文件)

SRC=动态的获取所有源文件(wildcard是Makefile自身语法包含的函数)

OBJ=(SRC:.c=.o表示将所有的.c换成.o形成可执行程序。这是Makefile自己的语法)

CC赋值为gcc

RM赋值为rm -f

$^:表示将所有的OBJ文件放到(CC)后,也可以理解为:$^就是OBJ

$@:表示我们要形成的目标文件

%.o:%叫通配符,也是makefile的语法,表示匹配任意内容。允许你匹配一组文件名,而不需要显式地列出每一个文件名

%.c:表示匹配所有的.c文件

$<:表示将所有的.c文件一个一个的交给对应的命令,一个一个的形成.o

@:不让echo回显

BIN=mytest2 #SRC=$(shell ls *.c)3 SRC=$(wildcard ls *.c)#动态的获取所有源文件(wildcard是Makefile自身语法包含的函数)4 OBJ=$(SRC:.c=.o)5 CC=gcc6 RM=rm -f7 8 $(BIN):$(OBJ)9   $(CC) $^ -o $@10   @echo "链接 $^ 成 $@"11 %.o:%.c12   $(CC) -c $<13   @echo "编译 $< 成 $@"                                                                                          14 15 .PHONY:clean16 clean:17   $(RM) $(OBJ) $(BIN)18 19 .PHONY:test20 test:21   @echo $(BIN)22   @echo $(SRC)23   @echo $(OBJ)

Linux第⼀个系统程序−进度条

回车与换行 

回车的概念

回车(Carriage Return,简称CR)起源于早期的打字机时代。在打字机上,有一个按键被称为回车键,按下这个按键会使打字机的滑块(或称为打印头)回到左侧边缘,同时纸张也会向上移动一行,以便继续输入文字。因此,这个按键被命名为“回车键”,因为它实现了“回车”到下一行的功能

换行的概念:光标换一行,同一列不同行

缓冲区

#include<stdio.h>
#include<unistd.h>
int main()
{//linux下一切皆文件,往显示器中打印就是往显示器中写入,加\n表示立即写入printf("hello world\n"); //没有\n,在linux下会先停两秒在输出                                                                                      sleep(2);return 0;
}

休眠两秒再出来是因为程序先执行的“sleep”吗?

c语言执行代码是自上向下执行的,不可能先执行sleep。当程序执行sleep休眠时,printf肯定已经执行完了

既然printf已经执行完了,为什么在sleep期间没有看到字符串打印?

此时字符串在一个叫做“缓冲区”的地方。这个缓冲区是为显示器提供的,既然有缓冲区,那必然就会有刷新策略,而显示器的刷新策略就是:行刷新。如果我碰到的打印的字符串包含“\n”,该消息就会立即显示到显示器上

如果不想换行,就让字符串立即打印到显示器上,怎么做?

用fflush()进行强制刷新。c语言在输入输出时,默认打开了三个文件的输入输出流:stdin(键盘)、stdout、stderr(这两个都是显示器)。printf进行打印时,实际上是把消息写到了stdout里面。如果消息没刷新出来,只要用fflush()刷新一下stdout就行

进度提条

小demo
#include<stdio.h>#include<unistd.h>int main(){int cnt=10;while(cnt>=0){//如果不加"2",从10-0输出则会呈现:10、90、80、...//因为%d的意思是将输出的内容进行格式化,就是将一个整数格式化为一个字符//10格式化后为两个字符,|1|0|。除去10之外的都是一个字符,只占一位,所有会出现10、90、80...的情况printf("%-2d\r",cnt);  //左对齐                                                                                      fflush(stdout);cnt--;sleep(1);}// printf("hello world");// fflush(stdout);//sleep(2);return 0;}
进度条
usleep:

1秒=1000毫秒=1000微妙*1000

main.c:
#include "process.h"
#include <unistd.h>
#include <time.h>
#include <stdlib.h>//函数指针类型
typedef void (*call_t)(const char*,double,double);double total = 1024.0;
//double speed = 1.0;
double speed[] = {1.0, 0.5, 0.3, 0.02, 0.1, 0.01};//回调函数
void download(int total, call_t cb)
{srand(time(NULL));double current = 0.0;while(current <= total){cb("下载中", total, current); // 进行回调if(current>=total) break;// 下载代码int random = rand()%6;usleep(5000);current += speed[random];if(current>=total) current = total;}
}void uploadload(int total, call_t cb)
{srand(time(NULL));double current = 0.0;while(current <= total){cb("上传中", total, current); // 进行回调if(current>=total) break;// 下载代码int random = rand()%6;usleep(5000);current += speed[random];if(current>=total) current = total;}
}int main()
{download(1024.0, FlushProcess);printf("download 1024.0MB done\n");download(512.0, FlushProcess);printf("download 512.0MB done\n");download(256.0,FlushProcess);printf("download 256.0MB done\n");download(128.0,FlushProcess);printf("download 128.0MB done\n");download(64.0,FlushProcess);printf("download 64.0MB done\n");uploadload(500.0, FlushProcess);return 0;
process.c:
#include "process.h"
#include <string.h>
#include <unistd.h>#define SIZE 101
#define STYLE '='//v2: 根据进度,动态刷新一次进度条
void FlushProcess(const char *tips, double total, double current)
{const char *lable = "|/-\\";int len = strlen(lable);static int index = 0;char buffer[SIZE];memset(buffer, 0, sizeof(buffer));double rate = current*100.0/total;int num = (int)rate;int i = 0;for(; i < num; i++)buffer[i] = STYLE;printf("%s...[%-100s][%.1lf%%][%c]\r", tips, buffer, rate, lable[index++]);fflush(stdout);index %= len;if(num >= 100)printf("\n");
}// v1: 展示进度条基本功能
void process()
{int rate = 0;char buffer[SIZE];memset(buffer, 0, sizeof(buffer));const char *lable = "|/-\\";int len = strlen(lable);while(rate <= 100){printf("[%-100s][%d%%][%c]\r", buffer, rate, lable[rate%len]);fflush(stdout);buffer[rate] = STYLE;rate++;usleep(10000);}printf("\n");
}

相关文章:

  • 【C++】小知识点
  • 《计算机组成原理》第 9 章 - 控制单元的功能
  • 电脑主板VGA长亮白灯
  • 自动化Web页面性能测试介绍
  • word中表格拉不动以及插入图片有间距
  • 使用 ssld 提取CMS 签名并重签名
  • Python学习(1) ----- Python的文件读取和写入
  • el-table设置自定义css
  • 电气行业PLM应用案例:国产PLM助力山西氪安研发转型
  • 高频面试--MySQL
  • day03
  • 大模型实现多卡训练保证数据一致性
  • SSM-IOC入门案例/DI入门案例
  • P4155 [SCOI2015] 国旗计划
  • 第二批考更有利?软考高项两个批次考试难度对比分析!
  • Oracle EBS 12.1 处理ISG 发布的wsdl 被请求时遇到500错误
  • K3s 中,CoreDNS 无法启动并报错 Listen: listen tcp :53: bind: permission denied
  • 机器学习中的关键术语及其含义
  • 永磁同步电机控制算法--基于电磁转矩反馈补偿的新型IP调节器
  • 鸿蒙OSUniApp 实现的数据可视化图表组件#三方框架 #Uniapp
  • 国外手机设计网站/哪里有学市场营销培训班
  • 用java后端做网站/深圳知名网络优化公司
  • 关于旅行的网站怎样做/seo排名优化什么意思
  • 巩义网站建设方案表/2345网址导航下载桌面
  • wordpress 卡蜜/seo是什么简称
  • 没有网站可以域名备案吗/中国万网域名注册免费