

文章目录
📖 前言
往期回顾
【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~我们下篇见 ✨

9095

被折叠的 条评论
为什么被折叠?



