C++ 日志系统实战第二步:不定参数函数解析
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~
相关技术知识补充
不定参宏函数
在 C 语言中,不定参宏函数是一种强大的工具,它允许宏接受可变数量的参数,类似于不定参函数,不过宏是在预处理阶段展开的。下面详细介绍不定参宏函数的使用,以自定义日志打印宏为例。
代码示例
#include <stdio.h>// 定义不定参宏函数,用于日志打印#define LOG(fmt, ...) printf("[%s:%d] " fmt, __FILE__, __LINE__, __VA_ARGS__)int main() {int number = 42;char name[] = "Alice";// 使用不定参宏函数进行日志打印LOG("The number is %d.\n", number);LOG("The name is %s.\n", name);LOG("Combined: %s's lucky number is %d.\n", name, number);return 0;}
代码解释
1. 宏定义部分
#define LOG(fmt, ...) printf("[%s:%d] " fmt, __FILE__, __LINE__, __VA_ARGS__)
- LOG 是宏的名称,它模拟了一个简单的日志打印功能。
- fmt 是一个固定参数,它代表了 printf 函数的格式控制字符串。
- ... 表示可变参数部分,意味着在调用 LOG 宏时可以传入任意数量的额外参数。
- __FILE__ 和 __LINE__ 是预定义的宏,__FILE__ 会在预处理时被替换为当前源文件的文件名,__LINE__ 会被替换为宏调用所在的代码行号。
- __VA_ARGS__ 是一个特殊的预定义宏,它会被替换为调用宏时传入的可变参数列表。
2. 主函数部分
int main() {int number = 42;char name[] = "Alice";LOG("The number is %d.\n", number);LOG("The name is %s.\n", name);LOG("Combined: %s's lucky number is %d.\n", name, number);return 0;}
- 首先定义了一个整数变量 number 和一个字符数组 name。
- 然后多次调用 LOG 宏进行日志打印,每次调用传入不同数量的参数。
- 第一次调用 LOG("The number is %d.\n", number); 时,fmt 被替换为 "The number is %d.\n",__VA_ARGS__ 被替换为 number。
- 第二次调用 LOG("The name is %s.\n", name); 时,fmt 被替换为 "The name is %s.\n",__VA_ARGS__ 被替换为 name。
- 第三次调用 LOG("Combined: %s's lucky number is %d.\n", name, number); 时,fmt 被替换为 "Combined: %s's lucky number is %d.\n",__VA_ARGS__ 被替换为 name, number。
输出结果
假设上述代码保存为 main.c,编译运行后可能的输出如下:
[main.c:11] The number is 42.[main.c:12] The name is Alice.[main.c:13] Combined: Alice's lucky number is 42.
这样,通过不定参宏函数,我们可以方便地在日志中记录文件名和行号信息,同时灵活处理不同数量的参数。
若要让 LOG 宏函数支持像 LOG("A charmer") 这种只传入一个参数的情况,就需要处理可变参数为空的情形。在 C 语言里,当可变参数为空时,__VA_ARGS__ 会在宏展开时产生一个多余的逗号,这会引发编译错误。为解决此问题,可借助 ## 操作符,它能在可变参数为空时去除多余的逗号。
#include <stdio.h>// 定义支持空可变参数的不定参宏函数#define LOG(fmt, ...) printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)int main() {// 传入多个参数LOG("The name is %s.\n", "A charmer");// 只传入一个参数LOG("A charmer\n");return 0;}
- 宏定义:#define LOG(fmt, ...) printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__),这里的 ## 操作符是关键,当可变参数 __VA_ARGS__ 为空时,它会移除前面多余的逗号,避免编译错误。
C风格不定参函数
在 C 语言里,不定参函数指的是可以接受可变数量参数的函数。为了实现不定参函数,需要用到 <stdarg.h>
头文件里的一些宏,这些宏能够帮助你访问可变参数列表。下面为你提供一个打印整数的不定参函数示例,同时会详细解释代码的逻辑。
#include <stdio.h>
#include <stdarg.h>// 不定参函数,用于打印多个整数
void print_integers(int count, ...) {// 定义 va_list 类型的变量,用于存储可变参数列表信息va_list args;// 初始化可变参数列表,count 为最后一个固定参数,表示可变参数的数量va_start(args, count);for (int i = 0; i < count; i++) {// 从可变参数列表中取出一个 int 类型的参数int num = va_arg(args, int);// 打印取出的整数printf("%d ", num);}// 换行printf("\n");// 结束对可变参数列表的使用,释放相关资源va_end(args);
}int main() {// 调用 print_integers 函数,传入可变参数的数量 5 以及 5 个整数print_integers(5, 1, 2, 3, 4, 5);return 0;
}
代码解释
-
头文件包含:
#include <stdio.h>
:提供标准输入输出函数,例如printf
。#include <stdarg.h>
:提供处理可变参数列表所需的宏和类型。
-
print_integers
函数:va_list args
:定义一个va_list
类型的变量args
,用于存储可变参数列表的信息。va_start(args, count)
:初始化可变参数列表,count
是最后一个固定参数,它表示后续可变参数的数量。va_arg(args, int)
:从可变参数列表中取出一个int
类型的参数。va_end(args)
:结束对可变参数列表的使用,释放相关资源。
-
main
函数:- 调用
print_integers
函数,传入可变参数的数量5
以及5
个整数1
、2
、3
、4
、5
。程序会将这些整数依次打印出来。
- 调用
这个示例展示了如何使用不定参函数来打印多个整数,你可以根据需求修改调用时传入的参数数量和具体整数值。
模拟实现printf
对字符串处理
下面是一个模拟实现 printf
函数的 C 语言代码示例,该示例支持 %d
(打印整数)、%s
(打印字符串)和 %c
(打印字符)这几种常见的格式说明符。
#include <stdio.h>
#include <stdarg.h>// 模拟实现 printf 函数
void my_printf(const char *format, ...) {va_list args;va_start(args, format);while (*format) {if (*format == '%') {format++;switch (*format) {case 'd': {int num = va_arg(args, int);printf("%d", num);break;}case 's': {char *str = va_arg(args, char *);printf("%s", str);break;}case 'c': {int ch = va_arg(args, int);printf("%c", (char)ch);break;}default:putchar(*format);break;}} else {putchar(*format);}format++;}va_end(args);
}int main() {int num = 123;char *str = "Hello";char ch = 'A';my_printf("Number: %d, String: %s, Character: %c\n", num, str, ch);return 0;
}
代码解释
-
头文件包含:
#include <stdio.h>
:提供标准输入输出函数,如printf
和putchar
。#include <stdarg.h>
:提供处理可变参数列表所需的宏和类型。
-
my_printf
函数:va_list args
:定义一个va_list
类型的变量args
,用于存储可变参数列表的信息。va_start(args, format)
:初始化可变参数列表,format
是最后一个固定参数。- 遍历
format
字符串:- 当遇到
%
时,检查下一个字符:- 如果是
d
,使用va_arg(args, int)
取出一个int
类型的参数并打印。 - 如果是
s
,使用va_arg(args, char *)
取出一个字符串指针并打印。 - 如果是
c
,使用va_arg(args, int)
取出一个字符(以int
类型存储)并打印。 - 对于其他字符,直接输出该字符。
- 如果是
- 如果不是
%
,直接输出该字符。
- 当遇到
va_end(args)
:结束对可变参数列表的使用,释放相关资源。
-
main
函数:- 定义一个整数
num
、一个字符串str
和一个字符ch
。 - 调用
my_printf
函数,传入格式化字符串和相应的参数。
- 定义一个整数
这个示例只是一个简单的模拟实现,真正的 printf
函数支持更多的格式说明符和复杂的功能。
用vasprintf接口⭐
vasprintf
是一个标准 C 库函数,它可以根据格式化字符串和可变参数列表动态分配内存并生成格式化后的字符串。下面是一个使用 vasprintf
接口模拟实现 printf
功能的示例代码:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>// 模拟实现 printf 函数,使用 vasprintf
void my_printf(const char *format, ...) {va_list args;va_start(args, format);char *output = NULL;// 使用 vasprintf 动态分配内存并格式化字符串if (vasprintf(&output, format, args) == -1) {perror("vasprintf");va_end(args);return;}// 输出格式化后的字符串printf("%s", output);// 释放动态分配的内存free(output);va_end(args);
}int main() {int num = 42;const char *str = "World";char ch = '!';my_printf("Hello, %s %d%c\n", str, num, ch);return 0;
}
代码解释
1. 头文件包含
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
stdio.h
:提供标准输入输出函数,如printf
和perror
。stdarg.h
:提供处理可变参数列表所需的宏和类型,如va_list
、va_start
、va_end
。stdlib.h
:提供动态内存分配和释放函数,如malloc
、free
,vasprintf
也依赖此头文件。
2. my_printf
函数
void my_printf(const char *format, ...) {va_list args;va_start(args, format);char *output = NULL;if (vasprintf(&output, format, args) == -1) {perror("vasprintf");va_end(args);return;}printf("%s", output);free(output);va_end(args);
}
va_list args
:定义一个va_list
类型的变量args
,用于存储可变参数列表的信息。va_start(args, format)
:初始化可变参数列表,format
是最后一个固定参数。vasprintf(&output, format, args)
:根据格式化字符串format
和可变参数列表args
动态分配内存并生成格式化后的字符串,将结果存储在output
指针指向的内存区域。如果分配内存失败,vasprintf
返回 -1。perror("vasprintf")
:如果vasprintf
失败,使用perror
输出错误信息。printf("%s", output)
:输出格式化后的字符串。free(output)
:释放vasprintf
动态分配的内存,避免内存泄漏。va_end(args)
:结束对可变参数列表的使用,释放相关资源。
3. main
函数
int main() {int num = 42;const char *str = "World";char ch = '!';my_printf("Hello, %s %d%c\n", str, num, ch);return 0;
}
- 定义一个整数
num
、一个字符串str
和一个字符ch
。- 调用
my_printf
函数,传入格式化字符串和相应的参数,模拟printf
函数的功能。
通过这种方式,我们利用 vasprintf
接口实现了一个简单的 printf
模拟函数。
C++风格不定参函数
#include <iostream>// 无参的 xprintf 函数,用于递归终止
void xprintf() {std::cout << std::endl;
}// 可变参数模板的 xprintf 函数
template<typename T, typename... Args>
void xprintf(const T &v, Args &&...args) {std::cout << v;if ((sizeof...(args)) > 0) {xprintf(std::forward<Args>(args)...);} else {xprintf();}
}int main() {xprintf(1);xprintf(1, 2, 3);return 0;
}
代码功能概述
这段 C++ 代码实现了一个名为 xprintf
的不定参数函数,其功能是将传入的不定数量的参数依次输出到控制台,每个 xprintf
调用结束后会换行。
- 模板参数:
template<typename T, typename... Args>
:定义了一个可变参数模板。T
代表第一个参数的类型,typename... Args
是可变参数包,它能容纳零个或多个不同类型的参数。- 函数参数:
const T &v
:第一个参数的常量引用,采用常量引用可避免不必要的拷贝,提高性能。Args &&...args
:可变参数包,使用右值引用(也叫万能引用),它既可以绑定左值,也可以绑定右值,并且结合std::forward
能实现完美转发。- 函数体逻辑:
std::cout << v;
:输出第一个参数。if ((sizeof...(args)) > 0)
:sizeof...(args)
用于获取可变参数包中参数的数量。若数量大于 0,说明还有剩余参数,就递归调用xprintf
函数,同时使用std::forward
对剩余参数进行完美转发,以保留参数的左值或右值属性。else
分支:若可变参数包为空,调用无参的xprintf()
函数,输出换行符,结束递归。
为什么要xprintf(),而不是直接cout<<endl?
当你调用
xprintf(1)
时,编译器会进行模板实例化的过程。在这个过程中:
- 模板参数推导:对于
template<typename T, typename... Args>
,根据传入的参数1
(类型为int
),T
被推导为int
。而Args
被推导为空参数包,因为此时只有一个参数1
传入,没有其他参数来构成可变参数包了。- 函数体执行:
std::cout << v;
这行代码会将v
(也就是1
)输出到标准输出流。- 接下来判断
if ((sizeof...(args)) > 0)
,由于Args
被推导为空参数包,sizeof...(args)
的值为0
,所以这个条件不成立,会执行else
分支。- 在
else
分支中,代码是std::cout << std::endl;
,这一步本身不会有问题,它会输出一个换行符。但是,这里存在一个潜在的问题,就是在这个函数模板中,
xprintf
函数本身是递归调用的(xprintf(std::forward<Args>(args)...);
这一行)。当编译器处理函数调用时,它需要知道在所有可能的情况下,xprintf
调用都有对应的函数定义可以匹配。在
xprintf(1)
这种情况下,虽然当前这次调用不会触发递归调用(因为可变参数包为空),但编译器在编译时并不能保证未来不会在其他情况下触发递归调用到一个无参数的xprintf
调用。也就是说,从编译器的角度来看,为了保证函数调用的完整性和正确性,它需要找到一个无参数的xprintf
函数定义,以便在递归过程中可能出现的无参数调用时能够正确解析。由于在没有定义无参
xprintf
函数的情况下,当编译器遇到这种可能的无参数调用情况(即使在当前调用中不会实际发生),它找不到合适的函数定义来匹配,就会报错,提示没有找到匹配的xprintf
函数。简单来说,就是编译器为了确保函数调用在各种情况下都能正确解析,要求有一个无参的
xprintf
函数定义来应对递归调用中可能出现的无参数调用场景,即使当前这次调用不会触发这个情况。
如果你对日志系统感到兴趣,欢迎关注我👉【A charmer】
后续我将继续带你实现日志系统~
相关文章:
C++ 日志系统实战第二步:不定参数函数解析
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~ 相关技术知识补充 不定参宏函数 在 C 语言中,不定参宏函数是一种强大的工具,它允许宏接受可变数量的参数,类似于不定参函数&#…...
【高并发】 MySQL锁优化策略
在数据库高并发场景中,行锁、表锁和高并发处理是密切相关的概念,它们共同影响着系统的并发性能和数据一致性。以下是三者的详细解释及高并发处理的策略: 1. 行锁(Row-Level Locking) 行锁是数据库中最小的锁粒度&…...
C语言——填充矩阵
C语言——填充矩阵 一、问题描述二、格式要求1.输入形式2.输出形式3.样例 三、实验代码 一、问题描述 编程实现自动填充nn矩阵元素数值,填充规则为:从第一行最后一列矩阵元素开始按逆时针方向螺旋式填充数值1,2,…,nn…...
CSS3 基础(背景-文本效果)
二、背景效果 属性功能示例值说明background设置背景颜色或渐变background: linear-gradient(45deg, #4CAF50, #FF5722);设置背景颜色、图片或渐变效果。background-size调整背景图片大小background-size: cover;设置背景图片的显示大小,如 cover 或 contain。back…...
点云配准算法之NDT算法原理详解
一、算法概述 NDT(Normal Distributions Transform)最初用于2D激光雷达地图构建(Biber & Straer, 2003),后扩展为3D点云配准。它将点云数据空间划分为网格单元(Voxel),在每个体…...
springboot在eclipse里面运行 run as 是Java Application还是 Maven
在 Eclipse 里运行 Spring Boot 项目时,既可以选择以“Java Application”方式运行,也可以通过 Maven 命令来运行,下面为你详细介绍这两种方式及适用场景。 以“Java Application”方式运行 操作步骤 在项目中找到带有 SpringBootApplicat…...
Redis 基础和高级用法入门
redis 是什么? Redis是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis提供了5种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:…...
使用vue2开发一个在线旅游预订平台-前端静态网站项目练习
hello,大家好,今天给大家再分享一个前端vue2练习项目-在线旅游预订平台。我们在学习编程的时候,除了学习编程的基础知识,为了让我们快速的掌握一门编程技术,肯定离不开各种项目的练习,今天分享的这个前端练习项目&…...
Ext Direct 功能与使用详解
Ext Direct 是 Ext JS 框架中的一个功能模块,旨在简化前端 JavaScript 应用与后端服务器之间的通信。其核心思想是通过远程过程调用(RPC)协议,将服务器端的方法透明地映射为前端可直接调用的 JavaScript 函数,从而减少手动编写 Ajax 请求和处理响应的代码量。 一、Ext Dir…...
Android移动应用开发入门示例:Activity跳转界面
介绍如何使用LinearLayout布局实现基本的UI设计,并实现两个Activity之间的跳转,适合刚接触Android Studio的新手学习。我们将使用Java语言开发,布局采用XML文件。以下为完整源码与运行说明: 案例前的准备工作: 1.1XM…...
【hadoop】HBase分布式数据库安装部署
一、HBase集群的安装与配置 步骤: 1、使用XFTP将HBase安装包hbase-1.2.0-bin.tar.gz发送到master机器的主目录。 2、解压安装包: tar -zxvf ~/hbase-1.2.0-bin.tar.gz 3、修改文件夹的名字,将其改为hbase,或者创建软连接也可…...
理解npm的工作原理:优化你的项目依赖管理流程
目录 什么是npm npm核心功能 npm 常用指令及其作用 执行npm i 发生了什么? 1. 解析命令与参数 2. 检查依赖文件 3. 依赖版本解析与树构建 4. 缓存检查与包下载 5. 解压包到 node_modules 6. 更新 package-lock.json 7. 处理特殊依赖类型 8. 执行生命周期脚本 9. …...
【Python笔记 04】输入函数、转义字符
一、Input 输入函数 prompt是提示,会在控制台显示,用作提示函数。 name input("请输入您的姓名:") print (name)提示你输入任意信息: 输入input test后回车,他输出input test 二、常用的转义字符 只讲…...
MySQL数据库基本操作-DQL-基本查询
数据库的操作中,查询是最重要的 一、基本查询-数据准备 -- 数据准备 create database if not exists mydb2; use mydb2; create table product( pid int primary key auto_increment, pname varchar(20) not null, price double, category_id varchar(20) …...
13、性能优化:魔法的流畅之道——React 19 memo/lazy
一、记忆封印术(React.memo) 1. 咒语本质 "memo是时间转换器的记忆晶石,冻结无意义的能量波动!" 通过浅层比较(shallowCompare)或自定义预言契约,阻止组件在props未变时重新渲染。 …...
低代码平台开发手机USB-HID调试助手
项目介绍 USB-HID调试助手是一种专门用于调试和测试USB-HID设备的软件工具。USB-HID设备是一类通过USB接口与计算机通信的设备,常见的HID设备包括键盘、鼠标、游戏控制器、以及一些专用的工业控制设备等。 主要功能包括: 数据监控:实时监控和…...
Langchain_Agent+数据库
本处使用Agent数据库,可以直接执行SQL语句。可以多次循环查询问题 前文通过chain去联系数据库并进行操作; 通过链的不断内嵌组合,生成SQL在执行SQL再返回。 初始化 import os from operator import itemgetterimport bs4 from langchain.ch…...
Code Splitting 分包策略
以下是关于分包策略(Code Splitting)的深度技术解析,涵盖原理、策略、工具实现及优化技巧: 一、分包核心价值与底层原理 1. 核心价值矩阵 维度未分包场景合理分包后首屏速度需加载全部资源仅加载关键资源缓存效率任意修改导致全量缓存失效按模块变更频率分层缓存并行加载单…...
AI 开发入门之 RAG 技术
目录 一、从一个简单的问题开始二、语言模型“闭卷考试”的困境三、RAG 是什么—LLM 的现实世界“外挂”四、RAG 的七步流程第一步:加载数据(Load)第二步:切分文本(Chunking)第三步:向量化&…...
day36图像处理OpenCV
文章目录 一、图像预处理18 模板匹配18.1模板匹配18.2 匹配方法18.2.1 平方差匹配18.2.2 归一化平方差匹配18.2.3 相关匹配18.2.4 归一化相关匹配18.2.5 相关系数匹配18.2.6 归一化相关系数匹配 18.3 绘制轮廓18.4案例 一、图像预处理 18 模板匹配 18.1模板匹配 模板匹配就是…...
系统与网络安全------弹性交换网络(3)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 STP协议 环路的危害 单点故障 PC之间的互通链路仅仅存在1个 任何一条链路出现问题,PC之间都会无法通信 解决办法 提高网络可靠性 增加冗余/备份链路 增加备份链路后交换网络上产生二层环路 …...
FPGA上实现YOLOv5的一般过程
在FPGA上实现YOLOv5 YOLO算法现在被工业界广泛的应用,虽说现在有很多的NPU供我们使用,但是我们为了自己去实现一个NPU所以在本文中去实现了一个可以在FPGA上运行的YOLOv5。 YOLOv5的开源代码链接为 https://github.com/ultralytics/yolov5 为了在FPGA中…...
verilog和system verilog常用数据类型以及常量汇总
int和unsigned 在 Verilog-2001 中,没有 int 和 unsigned 这样的数据类型。这些关键字是 SystemVerilog 的特性,而不是 Verilog-2001 的一部分。 Verilog-2001 的数据类型 在 Verilog-2001 中,支持的数据类型主要包括以下几种: …...
wordpress学习笔记
P1 P2 P3...
Rust 学习笔记:编程语言的相关概念
Rust 学习笔记:编程语言的相关概念 Rust 学习笔记:编程语言的相关概念动态类型 vs 静态类型动态类型 (Dynamically Typed)静态类型 (Statically Typed)对比示例 强类型 vs 弱类型强类型 (Strongly Typed)弱类型 (Weakly Typed)对比示例 编译型语言 vs 解…...
react nativeWebView跨页面通信
场景 react native项目里,有一些移动端的应用喜欢使用h5来开发,会出现需要跨tab和跨页面通信的场景,可以使用pubsub-js来实现通信。 实现思路 在react native 层实现pubsub的公共API,提供订阅消息、发布消息、取消订阅接口&…...
Python爬虫(3)HTML核心技巧:从零掌握class与id选择器,精准定位网页元素
目录 一、背景与意义二、class与id的基础概念与语法规则2.1 什么是class与id?2.2 核心区别总结 三、应用场景与实战案例3.1 场景1:CSS样式管理3.2 场景2:JavaScript交互3.3 场景3:SEO优化与语义化 四、常见误区与最…...
BGE(BAAI General Embedding)模型详解
BGE(BAAI General Embedding)模型详解 BGE(BAAI General Embedding)是北京智源人工智能研究院(BAAI)推出的通用文本嵌入模型系列,旨在为各种自然语言处理任务提供高质量的向量表示。 一、BGE模…...
【Linux网络】应用层自定义协议与序列化及Socket模拟封装
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
Rust项目GPG签名配置指南
Rust项目GPG签名配置指南 一、环境准备 # 安装Gpg4win(Windows) winget install -e --id GnuPG.Gpg4win二、密钥生成与配置 # 生成RSA4096密钥 gpg --full-generate-key # 类型选RSA and RSA,长度4096,邮箱填z3266420686202216…...
6.第六章:数据分类的技术体系
文章目录 6.1 数据分类的技术架构6.1.1 数据分类的整体流程6.1.2 数据分类的技术组件6.1.2.1 数据采集与预处理6.1.2.2 特征工程与选择6.1.2.3 分类模型构建6.1.2.4 模型评估与优化6.1.2.5 分类结果应用与反馈 6.2 数据分类的核心技术与算法6.2.1 传统机器学习算法6.2.2 深度学…...
Nginx 反向代理,啥是“反向代理“啊,为啥叫“反向“代理?而不叫“正向”代理?它能干哈?
Nginx 反向代理的理解与配置 User 我打包了我的前端vue项目,上传到服务器,在宝塔面板安装了nginx服务,配置了文件 nginx.txt .运行了项目。 我想清楚,什么是nginx反向代理?是nginx作为一个中介?中间件来集…...
下篇:深入剖析 BLE GATT / GAP / SMP 与应用层(约5000字)
引言 在 BLE 协议栈的最上层,GAP 定义设备角色与连接管理,GATT 构建服务与特征,SMP 负责安全保障,应用层则承载具体业务逻辑与 Profile。掌握这一层,可实现安全可靠的设备发现、配对、服务交互和定制化业务。本文将详解 GAP、GATT、SMP 三大模块,并通过示例、PlantUML 时…...
Linux Awk 深度解析:10个生产级自动化与云原生场景
看图猜诗,你有任何想法都可以在评论区留言哦~ 摘要 Awk 作为 Linux 文本处理三剑客中的“数据工程师”,凭借字段分割、模式匹配和数学运算三位一体的能力,成为处理结构化文本(日志、CSV、配置文件)的终极工具。本文聚…...
无人设备遥控之调度自动化技术篇
无人设备遥控器的调度自动化技术是现代科技发展的重要成果,它通过集成先进的通信、控制、传感器及人工智能技术,实现了对无人设备的高效、精准调度与自动化管理。 一、核心技术 无线通信技术 调度自动化依赖于高速、稳定的无线通信网络(如5…...
STM32F407 HAL库使用 DMA_Normal 模式实现 UART 循环发送(无需中断)
在 STM32 开发中,很多人喜欢使用 DMA 来加速串口发送数据。然而,默认的 DMA 往往配合中断或使用循环模式(DMA_CIRCULAR)使用。但在某些特定需求下,我们希望: 使用 DMA_NORMAL 模式,确保 DMA 每次…...
汽车自动驾驶介绍
0 Preface/Foreword 1 介绍 1.1 FSD FSD: Full Self-Driving,完全自动驾驶 (Tesla) 1.2 自动驾驶级别 L0 - L2:辅助驾驶L3:有条件自动驾驶L4/5 :高度/完全自动驾驶...
Uniapp-小程序从入门到精通
沉淀UNIAPP项目精华模版 ******************************************************************************************************************************************* 1、数据库的导入SQL **************************************************************************…...
深度剖析操作系统核心(第一节):从X86/ARM/MIPS处理器架构到虚拟内存、分段分页、Linux内存管理,再揭秘进程线程限制与优化秘籍,助你成为OS高手!
文章目录 OS处理器X86ARMMIPSPowerPC 内存管理虚拟内存内存分段内存分页段页式内存管理Linux 内存管理 OS 处理器 常见处理器有X86、ARM、MIPS、PowerPC四种。 X86 X86架构是芯片巨头Intel设计制造的一种微处理器体系结构的统称。如果这样说你不理解,那么当我说…...
基于 EFISH-SBC-RK3588 的无人机通信云端数据处理模块方案
一、硬件架构设计 核心计算单元(EFISH-SBC-RK3588) 异构计算能力:搭载 8 核 ARM 架构(4Cortex-A762.4GHz 4Cortex-A551.8GHz),集成 6 TOPS NPU 与 Mali-G610 GPU,支持多任务并行处理…...
Unity 内置Standard Shader UNITY_BRDF_PBS函数分析 (二)
四、BRDF1_Unity_PBS // 主物理基BRDF实现 // 基于Disney工作并以Torrance-Sparrow微面模型为基础 // 公式: // BRDF kD / π kS * (D * V * F) / 4 // I BRDF * (N L) // // * NDF(法线分布函数)可根据 UNITY_BRDF_GGX 选择&#…...
GitHub万星项目维护者分享:开源协作的避坑指南
GitHub万星项目维护者分享:开源协作的避坑指南 ——开发者张三与237个文件改动PR的五年战争 序幕:深夜的炸弹 2019年夏天,张三维护的开源项目TerminalX刚突破8000星,一个标题猩红的PR突然弹出:“彻底重构࿰…...
Linux基础篇、第四章_01软件安装rpm_yum_源码安装_二进制安装
Linux基础篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! ————laowang 基础命令:rpm、yum、源码安装、二进制安装 一、rpm本地安装: (无需网络安装,无法解决软件依赖) rpm -ivh …...
焊接机排错
焊接机 一、前定位后焊接 两个机台,①极柱定位,相机定位所有极柱点和mark点;②焊接机,相机定位mark点原理:极柱定位在成功定位到所有极柱点和mark点后,可以建立mark点和极柱点的关系。焊接机定位到mark点…...
4.2 Prompt工程与任务建模:高效提示词设计与任务拆解方法
提示词工程(Prompt Engineering)和任务建模(Task Modeling)已成为构建高效智能代理(Agent)系统的核心技术。提示词工程通过精心设计的自然语言提示词(Prompts),引导大型语…...
oracle 锁的添加方式和死锁的解决
DML锁添加方式 DML 锁可由一个用户进程以显式的方式加锁,也可通过某些 SQL 语句隐含方式实现。 DML 锁有三种加锁方式:共享锁方式、独占锁方式、共享更新。 共享锁,独占锁用于 TM 锁,共享锁用于 TX 锁。 1)共享方式的表级锁 共享方…...
Nginx 二进制部署与 Docker 部署深度对比
一、核心概念解析 1. 二进制部署 通过包管理器(如 apt/yum)或源码编译安装 Nginx,直接运行在宿主机上。其特点包括: 直接性:与操作系统深度绑定,直接使用系统库和内核功能 。定制化:支持通过…...
以太网的mac帧格式
一.以太网的mac帧 帧的要求 1.长度 2.物理层...
每日算法-250424
每日算法打卡 (24/04/25) - LeetCode 2971 & 1647 记录一下今天解决的两道 LeetCode 题目 2971. 找到最大周长的多边形 题目 思路 贪心 一个基本的多边形构成条件是:最长边必须小于其他所有边的长度之和。 为了找到周长最大的多边形,我们应该尽可能…...
在本地部署n8n:完整指南
n8n是一个强大的工作流自动化工具,可以帮助你连接不同的应用程序和服务,无需编写复杂的代码。本指南将带你完成在本地计算机上部署n8n的完整过程。 什么是n8n? n8n(发音为"n-eight-n")是一个开源的工作流自…...