Linux之基础开发工具二(makefile,git,gdb)

article/2025/6/23 2:34:57

目录

一、自动化构建-make/makefile

1.1、背景

1.2、基本使用

1.3、推导过程

1.4、语法拓展

二、进度条小程序

2.1、回车与换行

2.2、行缓冲区

2.3、练手-倒计时程序

2.4、进度条程序

三、版本控制器-Git

3.1、版本控制器

3.2、gitee的使用

3.2.1、如何创建仓库

3.2.2、如何将仓库克隆到本地

3.2.3、gitee三板斧

3.2.4、git pull

四、调试器-gdb/cgdb

4.1、预备

4.2、常见使用

4.3、常见技巧

4.3.1、watch

4.3.2、set var 确定问题原因

4.3.3、条件断点


一、自动化构建-make/makefile

1.1、背景

  • 会不会写makefile,从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒。
  • ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作。
  • makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全 ⾃动编译,极⼤的提⾼了软件开发的效率。
  • make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这个命令,⽐如:Delphi 的 make,Visual C++ 的nmake,Linux 下 GNU 的 make。可⻅,makefile 都成为了⼀种在⼯程⽅⾯的编译⽅法。
  • make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。

1.2、基本使用

示例代码:

makefile文件:

使用:

注释:在makefile文件中使用 # 进行注释。

依赖关系:

proc:proc.c 这一行叫做依赖关系,冒号右边的叫做依赖文件列表,依赖文件列表可以有一个或多个文件,也可以有零个文件,如 clean 这一行,它也是一行依赖关系,但它的依赖文件列表中没有文件。

依赖方法:

依赖关系下面缩进的部分就是依赖方法,表示执行依赖关系时执行的方法/指令,它必须以一个tab开头。gcc -o proc proc.c 和 rm -f proc 都是依赖方法。一个依赖关系下面可以有多行依赖方法,但都要以一个tab开头。依赖方法可以是任意指令。如图:

伪目标:

.PHONY表示声明一个伪目标,冒号后面跟伪目标的名称,需要执行它的依赖方法时只需要 make + 伪目标名称即可。.PHONY的作用是让被修饰的目标文件对应的方法总是被执行的。

makefile/make原理:

  1. makefile文件会被make从上向下扫描,第一个目标名是缺省形成的,即执行第一个依赖关系的方法时只需要make命令就行,但如果我们想执行其他组的依赖关系和依赖方法,需要make + 目标名。
  2. make,makefile在执行gcc命令时,如果发生了语法错误,就会终止推导过程。
  3. make解释makefile的时候,是会自动推导的。一直推导,推导过程,不执行依赖方法。直到推导到有依赖文件存在,然后再逆向的执行所有的依赖方法。
  4. make默认只形成一个可执行程序。

取消回显:

默认情况下,我们使用make命令时会回显依赖方法,如图:

  • makefile文件:

  • 使用:

想要取消回显,我们只需要在依赖方法前加@符号,如图:

  • makefile文件:

  • 使用:

项目清理:

  • ⼯程是需要被清理的。
  • 像clean这种,没有被第⼀个⽬标⽂件直接或间接关联,那么它后⾯所定义的命令将不会被⾃动执⾏,不过,我们可以显⽰要make执⾏。即命令⸺“make clean”,以此来清除所有的⽬标 ⽂件,以便重编译。
  • 但是⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤ .PHONY 修饰,伪⽬标的特性是,总是被执⾏的。

什么叫做总是被执行:

我们看下面示例

  • makefile文件:

  • 效果:

  • makefile文件:

  • 效果:

从上面两组示例可以看出,只有当编译指令所在的依赖关系被.PHONY修饰后,才能每次make都成功,这是为什么呢?其实,无论源文件还是最终生成的可执行文件本质都是文件,只要是文件就会有时间属性,如图:

上图中Access是文件最近被访问的时间,Modify是文件内容最近被修改的时间,Change是文件属性最近被修改的时间。决定一个文件是否需要被重新编译的是Modify时间,源文件和可执行程序都有自己的Modify时间,我们通过make执行编译命令时,它会自动对比这两个文件的Modify时间,如果可执行程序的时间新,就说明没有必要重新编译,也就如上面示例所示,编译命令不会执行,如果源文件的时间新,那就说明需要重新编译生成新的可执行程序,make命令就会成功,所以当我们不加.PHONY时,make命令成功与否取决于这两个时间的比较,而加上.PHONY后,make命令就会忽略时间的比较,直接去执行。

注意:有些命令需要比较时间,而有些命令本身就和时间无关,这些和时间无关的命令加不加.PHONY修饰都一样,但有些时候依赖方法有很多命令,只要有和时间有关的,就可能需要.PHONY修饰。

结论:

  • .PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐。

1.3、推导过程

下面我们看一下myproc.c生成myproc的完整推导过程:

myproc:myproc.o

        gcc myproc.o -o myproc

myproc.o:myproc.s

        gcc -c myproc.s -o myproc.o

myproc.s:myproc.i

        gcc -S myproc.i -o myproc.s

myproc.i:myproc.c

        gcc -E myproc.c -o myproc.i

.PHONY:clean

clean:

        rm -f *.i *.s *.o myproc

编译图解:

make是如何⼯作的,在默认的⽅式下,也就是我们只输⼊make命令。那么:

  1. make会在当前⽬录下找名字叫“Makefile”或“makefile”的⽂件。
  2. 如果找到,它会找⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会找到 myproc 这个⽂件,并把这个⽂件作为最终的⽬标⽂件。
  3. 如果myproc⽂件不存在,或是myproc所依赖的后⾯的 myproc.o ⽂件的⽂件修改时间要比myproc 这个⽂件新(可以⽤ touch 测试),那么,他就会执⾏后⾯所定义的命令来⽣成myproc 这个⽂件。
  4. 如果 myproc 所依赖的 myproc.o ⽂件不存在,那么 make 会在当前⽂件中找⽬标为myproc.o ⽂件的依赖性,如果找到则再根据那⼀个规则⽣成 myproc.o ⽂件。(这有点像⼀个堆栈的过程)
  5. 如果.o文件不存在,它会继续找生成.o文件所需的依赖文件,然后看该文件是否存在,如果存在就执行依赖方法生成,如果不存在则继续查找该文件的依赖文件。
  6. 在这个逐层查找的过程中,每找一层就会将它们的方法入栈,直到找到某一个依赖文件存在时,该依赖关系对应的依赖方法也进栈,之后就可以开始出栈了。
  7. 这就是整个make的依赖性,make会⼀层⼜⼀层地去找⽂件的依赖关系,直到最终编译出第⼀个⽬标⽂件。
  8. 在找寻的过程中,如果出现错误,⽐如最后被依赖的⽂件找不到,那么make就会直接退出,并报错,⽽对于所定义的命令的错误,或是编译不成功,make根本不理。
  9. make只管⽂件的依赖性,即,如果在我找了依赖关系之后,冒号后⾯的⽂件还是不在,那么对不起,我就不⼯作啦。

注意:上面这种写法太麻烦了,实践中一般不会这么写,一般只生成.o文件和可执行文件。

1.4、语法拓展

  • 变量:在makefile文件中可以定义变量,且变量没有类型。语法:变量名= ... 。
  • $(变量名):使用定义好的变量。
  • %:makefile文件中的通配符。
  • $<:把依赖文件依次放入指令中,放入一个执行一次指令。
  • $^:把依赖文件一次全部放入指令中,然后执行指令(只执行一次)。
  • $@:表示依赖关系中冒号左侧的内容。
  • @:取消指令回显

示例如下:

BIN=proc.exe                 # 定义变量

CC=gcc

#SRC=$(shell ls *.c)         # 采⽤shell命令⾏⽅式,获取当前所有.c⽂件名

SRC=$(wildcard *.c)         # 或者使⽤ wildcard 函数,获取当前所有.c⽂件名

OBJ=$(SRC:.c=.o)          # 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表

LFLAGS=-o                     # 链接选项

FLAGS=-c                       # 编译选项

RM=rm -f                         # 引⼊命令

$(BIN):$(OBJ)

        @$(CC) $(LFLAGS) $@ $^         # $@:代表⽬标⽂件名。 $^: 代表依赖⽂件列表

        @echo "linking ... $^ to $@ "

%.o:%.c                                        # %.c 展开当前⽬录下所有的.c。 %.o: 同时展开同名.o

        @$(CC)  $(FLAGS)  $<           # %对展开的依赖.c⽂件,⼀个⼀个的交给gcc。

        @echo "compling ... $< to $@ "         # @:不回显命令

.PHONY:clean

clean:

        $(RM) $(OBJ) $(BIN)

.PHONY:test

test:

        @echo $(SRC)

        @echo $(OBJ)

如何使用make命令形成多个可执行程序:

我们可以先定义一个依赖关系,它的依赖方法为空,它的依赖文件就是我们要生成的所有可执行程序,这样在推导时就会先去生成它的依赖文件,即我们需要的可执行程序,生成后因为它的依赖方法为空,make命令执行结束。如图:

  • makefile文件:

  • 效果:

二、进度条小程序

2.1、回车与换行

  • 回车概念:光标跳到当前行的起始位置。符号:\r。
  • 换行概念:光标相对于行起始位置的距离不变,跳转到下一行。符号:\n。

在C/C++语言中,对 \n 进行了处理,在语言上这一个符号即进行了回车,也进行了换行。

2.2、行缓冲区

示例代码一:

效果:先输入hello,Linux,再睡眠两秒。

示例代码二:

效果:先睡眠两秒,再输出hello,Linux。

是什么造成上面两段代码的差异的呢?首先,这两段代码的执行顺序都是一样的,都是先执行printf函数,再执行sleep函数。其次,我们要知道printf函数并不是直接将内容输出到显示器上,而是将内容输出到缓冲区,当缓冲区满了或者程序结束时会对缓冲区进行刷新,这里之所以会有差异是因为 \n 除了换行还会强制刷新缓冲区。

如何不通过\n强制刷新缓冲区:使用fflush函数。

2.3、练手-倒计时程序

  1 #include <stdio.h>2 #include <unistd.h>3 4 int main()5 {6     int count = 10;7     while(count >= 0)                                                                                                                                                           8     {9         printf("%-2d\r", count); // \r回车,但是没有换行,也就没有刷新10         fflush(stdout);11         count--;12         sleep(1);13     }14     printf("\r\n");15     return 0;16 }17 

解释:上面代码通过每次输出内容都带上 \r 来实现倒计时的数字在同一行,并且每次下一个数会在该行覆盖上一个数,不过需要注意的是,第一次输出的是10,占两位,后面输出的都是一位数字,因为每次还向显示器文件输出了回车,所以新的内容会在显示器文件起始处写入,这样每次都只能覆盖第一个数字,在显示器文件中0一直存在,导致我们看到的效果就是除10以外的其他数字后面也都跟着一个0。解决办法就是以%2d的格式向显示器文件进行写入,这样即使是一个数字也会占两位,这样就可以将0覆盖掉。但是因为C语言中默认这种格式是右对齐的,数字在右边,左侧会有空格,展现出来不好看,所以我们加一个负号,使其左对齐。

2.4、进度条程序

  • process.h:
#pragma once//version//void Process();
//void Process(double total, double current);
void FlushProcess(double total, double current);
  • process.c:
#include "process.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string.h>#define NUM 101
#define STYLE '='
#define POINT '.'
#define SPACE ' '
const int pnum = 6;// version 2:真实的进度条,应该根据具体的比如下载的量,来动态刷新进度
void FlushProcess(double total, double current)
{// 1. 更新当前进度的百分比double rate = (current/total)*100;// printf("test: %.1lf%%\r", rate);// fflush(stdout);// 2. 更新进度条主体char bar[NUM]; // 我们认为,1% 更新一个等号memset(bar, '\0', sizeof(bar));for(int i = 0; i < (int)rate; i++){bar[i] = STYLE;}// 3. 更新旋转光标或者是其他风格static int num = 0;num++;num%=pnum;char points[pnum+1];memset(points, '\0', sizeof(points));for(int i = 0; i < pnum; i++){if(i < num) points[i] = POINT;else points[i] = SPACE;}// 4. test && printfprintf("[%-100s][%.1lf%%]%s\r", bar, rate, points);fflush(stdout);//sleep(1);
}// version 1
//void Process()
//{
//    const char *lable = "|/-\\";
//    int len = strlen(lable);
//    char bar[NUM];
//    memset(bar, '\0', sizeof(bar));
//    int cnt = 0;
//    while(cnt <= 100)
//    {
//        printf("[%-100s][%d%%][%c]\r", bar, cnt, lable[cnt%len]);
//        fflush(stdout);
//        bar[cnt] = STYLE;
//        cnt++;
//        usleep(100000);
//    }
//
//    printf("\r\n");
//}
  • main.c:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include "process.h"typedef void (*flush_t)(double total, double current);// 这是一个刷新的函数指针类型const int base = 100;
double total = 2048.0; // 2048MB
double once = 0.1;     // 0.5MB// 进度条的调用方式
void download(flush_t f)
{double current = 0.0;while(current < total){// 模拟下载行为int r = rand() % base + 1; // [1, 10]double speed = r * once;current += speed;if(current >= total) current = total;usleep(10000);// 更新除了本次新的下载量// 根据真实的应用场景,进行动态刷新//Process(total, 1.0);f(total, current);//printf("test: %.1lf/%.1lf\r", current, total);//fflush(stdout);}printf("\n");
}int main()
{srand(time(NULL));download(FlushProcess);download(FlushProcess);download(FlushProcess);return 0;
}
  • makefile:
process:main.c process.cgcc -o $@ $^ -std=c99
.PHONY:clean
clean:rm -f process

效果:

三、版本控制器-Git

3.1、版本控制器

为了能够更⽅便我们管理这些不同版本的⽂件,便有了版本控制器。所谓的版本控制器,就是能让你 了解到⼀个⽂件的历史,以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业。

⽬前最主流的版本控制器就是Git。Git可以控制电脑上所有格式的⽂件,例如doc、excel、dwg、 dgn、rvt等等。对于我们开发⼈员来说,Git最重要的就是可以帮助我们管理软件开发项⽬中的源代码⽂件!

安装git:

yum install git            #centOS版本

3.2、gitee的使用

3.2.1、如何创建仓库

首先我们需要先注册一个gitee账号,然后登录。登录后点击右上角加号,点击新建仓库,如下图:

然后会进入如下界面:

这里仓库名称可以随意起,路径会自动生成,不用管,仓库介绍根据自己的实际情况写就可以。仓库可以开源也可以私有,根据情况选择。

建议对仓库进行初始化,希望仓库存储的是哪种语言的代码,就选择哪种语言就可以,.gitignore模版和语言选择一样的就行,开源许可证可以不选,对于模版的选择,如果这个仓库是自己用的选Readme就可以,后面两个多人协作会涉及,分支模型可以不选,不选默认单分支,如果需要多分支可以根据情况选择。然后点击创建就可以了。

3.2.2、如何将仓库克隆到本地

首先找到刚刚创建好的仓库,点击克隆/下载。如图:

点击后:

选择HTTPS,点击蓝框里的赋值按钮。然后回到Linux平台,输入命令:git clone + HTTPS链接。

然后该仓库就会被同步到本地了。

3.2.3、gitee三板斧

  • git add:

作用:将代码放到刚才下载好的⽬录中。

语法:git add [⽂件名]

  • git commit:

作用:提交改动到本地。

语法:git commit -m "XXX"

提交的时候应该注明提交⽇志,描述改动的详细内容。

  • git push:

作用:同步到远端服务器上。

语法:git push

需要填⼊用户名密码(gitee的)。同步成功后,刷新Gitee⻚⾯就能看到代码改动了。

配置免密提交:git本地免密码和账号pull、push-CSDN博客

注意:第一次提交会提示设置用户名和邮箱,邮箱要和注册gitee时的邮箱相同,否则看不到提交后的小绿点。

3.2.4、git pull

作用:将远端仓库同步到本地

语法:git pull

注意:当远端仓库和本地仓库不一致时,会导致无法提交,这时需要通过该指令使本地仓库和远端同步。

四、调试器-gdb/cgdb

4.1、预备

  • 程序的发布⽅式有两种, debug 模式和 release 模式, Linux gcc/g++ 出来的⼆进制程序,默认是 release 模式。
  • 要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候,加上 -g 选项,如果没有添加,程序⽆法被 编译。

$ gcc mycmd.c -o mycmd         # 默认模式,不⽀持调试

$ file mycmd

mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux3.2.0, not stripped

$ gcc mycmd.c -o mycmd  -g         # debug模式

$ file mycmd

mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux

3.2.0, with debug_info, not stripped

4.2、常见使用

开始:gdb binFile

退出:ctrl + d 或 quit 调试命令

命令作用样例
list/l显⽰源代码,从上次位置开始,每次列出10⾏list/l 10
list/l   函数名列出指定函数的源代码list/l  main
list/l ⽂件名:⾏号列出指定⽂件的源代码list/l mycmd.c:1
r/run从程序开始连续执⾏,遇到断点则停下,没有断点跑完程序run
n/next单步执⾏,不进⼊函数内部next
s/step单步执⾏,进⼊函数内部step
break/b  行号在指定行号设置断点b 10
break/b [⽂件名:]⾏号在指定文件的指定⾏号设置断点break 10 ;break test.c:10
break/b 函数名在函数开头设置断点break main
info break/b查看当前所有断点的信息info break/b
finish执⾏到当前函数返回,然后停⽌finish
print/p 表达式打印表达式的值print start+end
p 变量打印指定变量的值p x
set var 变量=值修改变量的值set var i=10
continue/c从当前位置开始连续执⾏程序continue
delete/d breakpoints删除所有断点delete breakpoints
delete/d breakpoints n删除序号为n的断点(breakpoints可不加)delete  (breakpoints) 1
disable breakpoints禁⽤所有断点disable breakpoints
enable breakpoints启⽤所有断点enable breakpoints
info/i breakpoints查看当前设置的断点列表info breakpoints
display 变量名跟踪显⽰指定变量的值(每次停⽌时)display x
undisplay 编号取消对指定编号的变量的跟踪显⽰undisplay 1
until X⾏号执⾏到指定⾏号until 20
backtrace/bt查看当前执⾏栈的各级函数调⽤及参数backtrace
info/i locals查看当前栈帧的局部变量值info locals
quit退出GDB调试器quit

注意:

  • gdb会记录最新的一条命令,按回车默认是执行该命令。如 list 1 后按回车,会从上次代码结束的位置继续显示。
  • 在一个调试周期内,断点的编号是递增的。例如我们设置了两个断点,它们的序号是1,2,然后我们删除它们在重新设置断点,这时断点的编号是从3开始的。
  • 禁用和启用断点的关键字(disable、enable)后可以直接加断点编号,表示禁用或启用某一个断点。

4.3、常见技巧

上⾯的基本调试还是麻烦,虽然是⿊屏,但是还是想看到代码调试,推荐安装cgdb:

Ubuntu: sudo apt-get  install  -y  cgdb

Centos:  sudo yum  install  -y  cgdb

4.3.1、watch

执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB会暂停程序的执⾏,并通知使⽤者。

(gdb) watch result

Hardware watchpoint 2: result

(gdb) c

Continuing.

Hardware watchpoint 2: result

Old value = 0

New value = 1

如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你。

4.3.2、set var 确定问题原因

作用:更改某个变量值,帮助我们进一步锁定问题。

(gdb) set var flag=1         # 更改flag的值,确认是否是它的原因

有时我们调试bug时可能发现是某一个变量值的错误导致的问题,但我们无法确定判断是否准确,这时我们可以通过 set var 修改变量值,在重新通过 run 命令再次调试看结果来观察我们的判断是否准确。这样的好处是避免频繁退出调试去修改代码。

4.3.3、条件断点

  • 添加条件断点:

(gdb) b 9 if i == 30         # 9是⾏号,表⽰新增断点的位置

  • 给已经存在的断点新增条件:

(gdb) info b

Num                 Type                 Disp Enb Address                                 What

1                 breakpoint        keep y  0x00005555555551c3          in main at mycmd.c:20

                   breakpoint already hit 1 time

2                 breakpoint        keep y 0x0000555555555186           in Sum at mycmd.c:9

(gdb) condition 2 i==30         #给2号断点,新增条件i==30

注意:

  • 条件断点添加常⻅两种⽅式:1. 新增 2. 给已有断点追加。注意两者的语法有区别,不要写错了。
  • 新增:b ⾏号/⽂件名 : ⾏号/函数名  if  i  ==  30(条件)
  • 给已有断点追加:condition  2  i==30,其中2是已有断点编号,没有if。

http://www.hkcw.cn/article/kJmDZPwLXJ.shtml

相关文章

如何使用gitee进行代码管理(常见的两种私人令牌-HTTPS和公钥SSH)

Getee平台提供了四种方式管理代码&#xff0c;如下图所示&#xff1a; 一、使用私人令牌&#xff08;HTTPS&#xff09;管理代码 优点&#xff1a;账户下所有项目都可以操作&#xff0c;并且使用快捷&#xff0c;过程简单&#xff0c;可以选择令牌的权限范围&#xff0c;HTTPS…

@PathVariable注解-补充

这段代码是 Spring MVC 框架中使用 RESTful 风格的请求处理方法&#xff0c;详细解释其功能和注解&#xff1a; 代码功能概述 这段 Java 代码定义了一个 Spring MVC 控制器方法&#xff0c;用于处理 RESTful 风格的 URL 请求。它可以从 URL 路径中提取参数&#xff0c;并将这…

Canvas实例篇:十二星座之天秤座

Canvas实例篇&#xff1a;十二星座之天秤座 前言效果预览代码实现代码说明星座特定星 结语 前言 星座总给人浪漫而神秘的感觉&#xff0c;如何用代码还原星空中的浪漫&#xff1f;本文将通过 Canvas 技术&#xff0c;讲述如何实现一个可交互的天秤座星空图&#xff0c;包含星星…

VIP》》IP地址漂移

IP地址漂移&#xff0c;就是一个虚拟的IP地址&#xff0c;能够在不同的物理服务器或网络接口之家来回转换&#xff0c;所以当你或者其他的网络设备跟这个虚拟IP地址连接的时候&#xff0c;并不会察觉到设备的转换。这对于网络流量调度&#xff0c;服务器负载均衡的使用意义重大…

【C语言】讲解 程序分配的区域(新手)

目录 代码区 数据区 堆区 栈区 常量区 重点比较一下堆区与 栈区 总结&#xff1a; 前言&#xff1a; C语言程序的内存分配区域是理解其运行机制的重要部分。根据提供的多条证据&#xff0c;我们可以总结出C语言程序在运行时主要涉及以下五个关键内存区域&#xff1a; 代…

时间序列预测入门喂饭教程,python代码示例

目录 前言一、基本原理1、啥是时间序列&#xff1f;2、预测前的准备什么材料&#xff1f;3、搭建你的预测工具&#xff08;模型&#xff09; 二、建模模拟实战&#xff1a;用Python搭个简单模型**Step 1&#xff1a;先把需要的工具库准备好****Step 2&#xff1a;生成一组模拟的…

BLE 广播与扫描机制详解:如何让设备“被看见”?

在 BLE 通信中,“广播”是设备展示自己的方式,“扫描”是发现外设的入口。 作为 BLE 协议的核心机制之一,广播与扫描的设计直接影响通信的稳定性、功耗与连接效率。本篇将从 BLE 广播/扫描原理、数据结构、事件流程到调试技巧全面展开,配合实战案例深入讲解 BLE 设备“可被…

小程序使用npm包的方法

有用的链接 npm init -y 这个命令很重要, 会初始化 package.json 再重新打开微信小程序开发工具 选择工具中npm构建 在程序中引用时在main.js中直接使用包名的方式引用即可 如安装的是generator包&#xff0c;npm构建后就会生成 const myPackage require(***-generato…

Java 单例模式详解

目录 1. 饿汉式&#xff08;Eager Initialization&#xff09; 2. 懒汉式&#xff08;Lazy Initialization&#xff09; 3. 懒汉式 同步锁&#xff08;线程安全&#xff09; 4. 双重检查锁&#xff08;Double-Checked Locking&#xff09; 5. 静态内部类&#xff08;推荐…

大模型应用开发之预训练

预训练是研发大语言模型的第一个训练阶段&#xff0c;通过在大规模语料上进行预训练&#xff0c;大语言模型可以获得通用的语言理解与生成能力&#xff0c;掌握较为广泛的世界知识&#xff0c;具备解决众多下游任务的性能潜力 一、数据预处理 1. 数据的收集 1&#xff09;通…

属性映射框架-MapStruct

属性映射框架-MapStruct 文章目录 属性映射框架-MapStruct一、作用二、MapStruct 简介2.1 是什么2.2 竞品框架2.3 适合场景 三、入门案例3.1 项目需求3.2 代码实现 四、入门案例解析五、MapStruct 实战5.1 当属性正常映射时5.2 当某个属性要忽略映射5.3 当某个属性要求设置默认…

switch-case判断

switch-case判断 #include <stdio.h> int main() {int type;printf("请输入你的选择&#xff1a;\n");scanf("%d",&type);getchar();switch (type){case 1:printf("你好&#xff01;");break;case 2:printf("早上好&#xff01;…

德拜温度热容推导

目录 一、背景与基本假设 一、态密度的定义 二、从波矢空间出发 三、振动模式数与波矢体积关系 四、模式总数计算 五、态密度求导 六、德拜频率确定与归一化条件 二、内能表达式的推导 三、态密度代入与变量替换 四、求比热容 五、低温时&#xff08;&#xff09; …

Android Framework层RenderThread指令队列深度调试实战指南

简介 在移动应用开发过程中,UI渲染性能优化是提升用户体验的关键环节。Android的RenderThread作为硬件加速渲染的核心线程,其指令队列的处理效率直接影响着应用的流畅度。本篇文章将深入探讨如何在Android Framework层对RenderThread指令队列进行调试和优化,帮助开发者解决…

BLE协议全景图:从0开始理解低功耗蓝牙

BLE(Bluetooth Low Energy)作为一种针对低功耗场景优化的通信协议,已经广泛应用于智能穿戴、工业追踪、智能家居、医疗设备等领域。 本文是《BLE 协议实战详解》系列的第一篇,将从 BLE 的发展历史、协议栈结构、核心机制和应用领域出发,为后续工程实战打下全面认知基础。 …

深入理解C#异步编程:原理、实践与最佳方案

在现代软件开发中&#xff0c;应用程序的性能和响应能力至关重要。特别是在处理I/O密集型操作&#xff08;如网络请求、文件读写、数据库查询&#xff09;时&#xff0c;传统的同步编程方式会导致线程阻塞&#xff0c;降低程序的吞吐量。C# 的异步编程模型&#xff08;async/aw…

如何查看电脑电池性能

检查电脑电池性能的方法如下&#xff1a; 按下winR键&#xff0c;输入cmd回车&#xff0c;进入命令行窗口 在命令行窗口输入powercfg /batteryreport 桌面双击此电脑&#xff0c;把刚刚复制的路径粘贴到文件路径栏&#xff0c;然后回车 回车后会自动用浏览器打开该报告 红…

高考加油!UI界面生成器!

这个高考助力标语生成器具有以下特点&#xff1a; 视觉设计&#xff1a;采用了蓝色为主色调&#xff0c;搭配渐变背景和圆形装饰元素&#xff0c;营造出宁静而充满希望的氛围&#xff0c;非常适合高考主题。 标语生成&#xff1a;内置了超过 100 条精心挑选的高考加油标语&a…

fork函数小解

学了好久终于搞懂fork函数的一些作用 1. fork函数作用&#xff1a;用于创建新的子进程 这是fork最根本的功能&#xff0c;在父进程里创建新的子进程、 但是创建新的子进程之后呢&#xff1f; 子进程和父进程的关系是什么样的&#xff1f; 为什么fork得到的子进程返回值为0&am…

5月31日day41打卡

简单CNN 知识回顾 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常见流程如下&#xff1a; 1. 输入 → 卷积层 → Batch…