库:

  • 动态库动态库(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=mytest
  2 #SRC=$(shell ls *.c)
  3 SRC=$(wildcard ls *.c)#动态的获取所有源文件(wildcard是Makefile自身语法包含的函数)
  4 OBJ=$(SRC:.c=.o)
  5 CC=gcc
  6 RM=rm -f
  7 
  8 $(BIN):$(OBJ)
  9   $(CC) $^ -o $@
 10   @echo "链接 $^ 成 $@"
 11 %.o:%.c
 12   $(CC) -c $<
 13   @echo "编译 $< 成 $@"                                                                                          
 14 
 15 .PHONY:clean
 16 clean:
 17   $(RM) $(OBJ) $(BIN)
 18 
 19 .PHONY:test
 20 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");
}

Logo

更多推荐