【Linux基础】条件编译|静态库/动态库区别|Makefile自动化构建|手写进度条(附源码)


在这里插入图片描述


🎬 博主名称键盘敲碎了雾霭

🔥 个人专栏: 《C语言》《数据结构》 《C++》 《Matlab》 《Python》 《Linux》

⛺️指尖敲代码,雾霭皆可破

在这里插入图片描述

📖 前言

往期回顾
【Linux基础】包管理器、Vim编辑器与GCC编译链完全指南
【Linux基础】指令、shell原理与权限管理详解

这篇文章是我在学习过程中整理的笔记与实践总结,尽量用通俗的语言 + 可运行的代码 + 直观的动图,把每个知识点讲清楚。如果你也正在啃Linux相关内容的部分,希望这篇内容能帮你少走一些弯路。

一、条件编译

1.1 gcc添加宏

先建立code.c

> code.c

再打开文件

vim code.c

输入以下

#include<stdio.h>
//#define M 9
int main()
{
#ifndef M
    printf("专业版\n");
#else
    printf("免费版\n");
#endif
}

gcc可以在命令行添加宏(选项D)

gcc code.c -DM=10

1.2 用途

  • 软件进行专业度,收费情况进行区分(业务),使用条件编译,可以进行代码动态裁减
  • 内核源代码也是采用条件编译进行代码裁减
  • 开发工具,应用软件

二、静态库和动态库

2.1 概念

Linux下,动态库XXX.so,静态库XXX.a
Windows下,动态库XXX.dll,静态库XXX.lib
库的命名:去掉前缀(lib)、去掉后缀(.so),剩下的就是名字(可以判断是c语言库)

  • 动态链接
    动态库内部实现的方法:我们自己的程序,会使用库中的方法,让我们自己的程序
    能在库中找到方法、形成可执行程序(执行目标方法,需要跳转到库中执行,完了再返回)
  • 静态链接
    把程序所要的库实现方法拷贝一份到源代码

2.2 验证

动静态库对比:

  • 动态库形成的可执行程序体积一定很小。
  • 可执行程序对静态库的依赖度小,动态库不能缺失。
  • 程序运行,需要加载到内存,静态链接的,会在内存中出现大量的重复代码。
  • 动态链接,比较节省内存和磁盘资源

gcc编译默认使用动态链接,使用-static使用静态链接
file a.out:可以查看可执行文件的属性,也可以看后缀确定是动态链接还是静态链接

在这里插入图片描述
gcc code.c -o code -static用静态库编译,可能会报错,需要安装对应的C语言静态库,然后执行查看属性

在这里插入图片描述
ldd code:查看所需要的链接库(静态链接是直接拷贝,因此没有所对应的链接库)
在这里插入图片描述

对比验证可以看到动态链接,比较节省内存和磁盘资源

在这里插入图片描述
注意:

  • 同理C++也一样
  • 动态库的本质:是把语言层面公共的代码,在内存中未来只出现一份 (共享库)!

2.3 修改sudo文件

有时候使用sudo命令没法使用,就需要修改

在这里插入图片描述
查看相关文件

在这里插入图片描述
用vim打开也没权限

在这里插入图片描述
切换root用户,修改文件
su -
vim /etc/sudoers

在这里插入图片描述
重新运行sodu命令,就可以执行成功

在这里插入图片描述

2.4 进一步理解库

一般.c编译成.o文件,再链接形成.exe文件
动静态库的本质就是打包后的.o文件,链接就是把.o文件进行合并
在这里插入图片描述
这样在某些场景下可以隐藏.c文件,暴露.o文件给对方
在这里插入图片描述

三、自动化构建-make/Makefile

3.1 概念

  • make是一个命令
  • makefile是一个文件
    在这里插入图片描述

make命令会在当前目录下找一个文件Makefile(不区分大小写),会自动推导形成文件和方案
注意:makefile必须有依赖关系,依赖方法
在这里插入图片描述

修改makefile文件

.PHONY:clean
clean:
	rm -rf mycode
mycode:mycode.c
	gcc mycode.c -o mycode

make命令扫描makefile文件的时候,从上向下扫描,默认形成第一个目标文件(后面执行make需要加目标文件)
在这里插入图片描述
.PHONY修饰的为伪目标,总是被执行,对应的依赖方法和依赖关系
由于mycode没被修饰,所以不支持多次执行,,默认执行程序不需要加.PHONY,会增加编译时间和成本

在这里插入图片描述

什么叫做不被执行呢

  • 默认老代码不做重新编译
  • make指令怎么知道.exe和.c的新旧问题?

回答这个答案需要了解时间相关知识

3.2 文件属性——时间

stat可以查看更多属性,文件=内容+属性
在这里插入图片描述

  • Access
    查看文件(特殊),由于访问的次数比修改次数比重大,于是设定查看多少次才会更新时间

  • Modify
    修改文件内容的时间(由于size变化,Change时间也会发生变化,并且Modify时间本身就是属性,Change时间也会发生变化)

  • Change
    只修改文件属性的时间
    在这里插入图片描述

make指令是看Modify时间,一般来说.c文件是早于.exe文件的,通过这个可以判断
在这里插入图片描述

make不能编译时,touch可以更新时间,继续编译

在这里插入图片描述
PHONY的作用就是make调用gcc告诉gcc忽略对比时间
补充:在makefile注释代码用#

3.3 makefile规则

在makefile输入这段代码,执行make
在这里插入图片描述
make内部像维护一个栈一样,会依次往下找,知道文件存在,执行并去除栈里面的指令
在这里插入图片描述

3.4 定义变量

在makefile输入

BIN=mycode
SRC=mycode.c
CC=gcc
FLAGS=-o
RM=rm -rf

$(BIN):$(SRC)
	@$(CC) $^ $(FLAGS) $@
	@echo "$^ to $@"

.PHONY:clean
clean:
	@$(RM) $(BIN)
	@echo "clean $(BIN)"

.PHONY:test
test:
	@echo $(BIN)
	@echo $(SRC)
	@echo $(CC)
	@echo $(FLAGS)

补充:

  • $(变量)相当与宏替换,在指令前加上@可以实现不输出指令(@:不回显命令)
  • 在依赖方法中,$@代表目标文件名,$^代表依赖文件列表

3.5 多个文件

正常有多个文件需要先一个个变成.o文件,再链接成.exe
%.c 展开当前目录下所有的.c。%.o:同时展开同
在这里插入图片描述
如果当前有100个文件不可能全部列出来
有两种方法

  • $(shell ls *.c)
  • $(wildcard *.c)
    其中OBJ(.o)文件$(SRC:.c=.o)

在这里插入图片描述
上面形成了一个比较完整的makefile文件

BIN=mycode.exe
SCR=$(shell ls *.c)
OBJ=$(SCR:.c=.o)
CC=gcc
FLAG=-o
LFLAG=-c
RM=rm -rf

$(BIN):$(OBJ)
	@$(CC) $(FLAG) $@ $^
	@echo "link $(OBJ) to $(BIN)"
%.o:%.c
	@$(CC) $(LFLAG) $<
	@echo "compling $< to $@"
.PHONG:clean
clean:
	@$(RM) $(BIN) $(OBJ)
.PHONG:test
test:
	@echo "$(SCR)"
	@echo "$(OBJ)"

运行结果
在这里插入图片描述

四、进度条

4.1 预备知识

  • 回车换行
    /r/n,C语言中的/n编译时当作/r/n
  • 缓冲区问题
    先可以当作一块内存块来理解,分别执行1、2句代码在这里插入图片描述

发现执行2时,printf执行完了!显示器没有显示,那么在我们休眠3s期间,"hellobit”在哪里?

缓冲区里面,如果加上\n,缓冲区是进行行刷新,会刷新到显示器
程序执行默认打开三个IO流
在这里插入图片描述
也可以使用fflush(stdout)刷新显示器

在这里插入图片描述

4.2 倒计时

结合预备知识,实现在原位置进行倒计时的效果

  • code.c
#include<stdio.h>
#include<unistd.h>
int main()
{
    int n=10;
    while(n>=0)
    {
        printf("%-2d\r",n);
        fflush(stdout);
        sleep(1);
        n--;
    }
    printf("\n");
}
  • makefile
BIN=mycode.exe
SRC=$(shell ls *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
LFLAG=-o
FLAG=-c

$(BIN):$(OBJ)
	@$(CC) $(LFLAG) $@ $^
	@echo "Link ...... $^ to $@"

%.o:%.c
	$(CC) $(FLAG) $<
	@echo "compling ...... $< to $@"

4.3 进度条(原理版本)

  • progress.h
#pragma once
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define M 101
#define STY '#'
void progress();
  • progress.c
#include"progress_bar.h"
void progress()
{
    char buff[M]={0};
    int n=0;
    char* sty="|/-\\\\";
    int len=strlen(sty);
    while(n<=100)
    {
        printf("[%-100s][%d%%][%c]\r",buff,n,sty[n%len]);
        fflush(stdout);
        buff[n]=STY;
        n++;
        usleep(500000);
    }
    printf("\n");
}
  • main.c
#include"progress.h"
int main()
{
    progress();
}

make执行下,结果如下
在这里插入图片描述
这个版本的进度条只适合玩,具体项目没法调用

4.4 进度条(真实版本)

场景:一边下载(模拟网络代码),边更新进度条,进度条不应该直接跑完

注意:显示进度的进度按照调用进度条次数有关,与是否下载成功或失败无关

  • main.c
#include"progress.h"
void Download()
{
    double total =1024.0;
    double speed=1.0;
    double cur=0.0;
    while(cur<=total)
    {
        progress_bar(cur,total);
        usleep(3000);//下载消耗时间
        cur+=speed;
    }
    printf("\n下载完成%.2lf\n",total);
}
int main()
{
    Download();
    Download();
    Download();
    Download();
    Download();
}
  • progress.h
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define M 101
#define STY '#'
void progress_bar(double cur,double total);
  • progress.c
#include"progress.h"

void progress_bar(double cur,double total)
{
    char buff[M]={0};
    char* sty="/-|\\\\";
    int len =strlen(sty);
    int num =cur/(total/100);
    for(int i=0;i<num;i++)
    {
        buff[i]=STY;
    }
    static int count=0;
    count%=len;
    double rat=cur/total;
    printf("[%-100s][%.2lf%%][%c]\r",buff,rat*100,sty[count]);
    fflush(stdout);
    count++;
}

效果演示
在这里插入图片描述


文章结语

感谢你读到这里~我是「键盘敲碎了雾霭」,愿这篇文字帮你敲开了技术里的小迷雾 💻

如果内容对你有一点点帮助,不妨给个暖心三连吧👇
👍 点赞 | ❤️ 收藏 | ⭐ 关注
(听说三连的小伙伴,代码一次编译过,bug绕着走~)

你的支持,就是我继续敲碎技术雾霭的最大动力 🚀

🐶 小彩蛋:

      /^ ^\
     / 0 0 \
     V\ Y /V
      / - \
    /    |
   V__) ||

摸一摸毛茸茸的小狗,赶走所有疲惫和bug~我们下篇见 ✨

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值