源码学习初章-基础知识储备


文章目录

  • 学前准备
    • 源码地址
    • 引言
    • extern "C"
  • 宏定义
    • 平台宏
      • 跨平台宏
      • vstdio平台禁用警告宏
    • 连接、双层宏定义
    • 函数宏
      • 系统函数宏
      • 自定义函数宏
      • 多语句执行宏do while0
    • 普通宏定义
  • C的一些必备函数知识
    • 回调函数和函数指针
      • 回调函数wireshark-4.0.7源码例子
      • 函数指针wireshark4.0.7源码例子
  • 结构体和关键字
    • 结构体和联合体
      • 结构体 struct
      • 联合体 union
    • 关键字
      • static
      • extern
      • C++关键字 explicit
  • 标志位与位操作
    • & 、 |、<<、>>
    • 标志位(|加标志)(&减/检标志)
  • 代码习惯
    • if else
    • if else break
    • 函数定义
  • 其他内容后续补充


学前准备

源码地址

cJSON库地址
sqlite-github源码地址
sqlite官方网址
wireshark源码

引言

  • 引言参考的是sqlite和wireshark的部分代码,第二篇开始进入cJSON正式篇
  • 第一篇暂时不涉及github的源码,参考的是sqlite官网发布的shell.c sqlite3.h .c等文件进行简单的知识储备
  • 另外由于工作中多是linux环境所以把vstdio卸载了,但是为了在windows下学习sqlite源码所以我采用的是Qt5.14.1 MinGW32进行开发
  • 本文只会使用cpp的new和delete,其他语法将均为c的语法
  • 面向对象只是一种设计模式,如果你愿意,你可以用汇编来实现面向对象

extern “C”

  • 声明:
    这么做的目的是把c作为底层开发库,这样任何一个编程语言都可以调用c写的库了
  • 区别
    1、因为在 C 语言中,默认的函数调用约定是 C 调用约定(C calling convention),参数通过栈传递,由调用者清理栈。
    2、在 C++ 中,默认的函数调用约定是 C++ 调用约定(C++ calling convention),其实质是通过名称修饰(Name Mangling)来支持函数重载,参数传递方式和 C 调用约定类似,但由于函数名被修饰,因此 C++ 编译器能够识别重载的函数
  • 做法
    在头文件中 所有的 函数 结构体 变量 等能想到的所有内容都放在两个 "{}"中
#ifdef __cplusplus
extern "C" {
#endif
void fun1();
void fun2();
static struct Stu{int a;char b;
}stu{0,0};
int a=10;
#ifdef __cplusplus
}
#endif

宏定义

平台宏

跨平台宏

#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <windows.h>
#elif define(__linux__)
#include <socket.h>
#endif

总结:
- !defined(_CRT_SECURE_NO_WARNINGS)是为了防止C4996报错
- defined(_WIN32)和defined(WIN32)用来判断是否是windows,defined(WIN64)是在win系统基础上判断是不是win64位

vstdio平台禁用警告宏

#if defined(_MSC_VER)
#pragma warning(disable : 4054)// 禁用类型转换警告。此警告提示可能存在的类型转换问题。
#pragma warning(disable : 4055)//禁用类型转换警告。此警告提示可能存在的类型转换问题。
#pragma warning(disable : 4100)//禁用未使用的形参警告。此警告提示声明了但未在函数体内使用的形式参数。
//等
#endif /* defined(_MSC_VER) */

连接、双层宏定义

#include <stdio.h>// 定义连接运算符宏
#define CONCAT(x, y) x##y//双层宏定义
/** 双重宏的第一个宏用于接收参数,并进行预处理操作。* 第二个宏则负责将经过预处理的参数传递给其他宏或函数,或者用于宏展开后的标识符构造* */
// 第一个宏,用于将宏参数转换为字符串常量
#define SHELL_STRINGIFY_(f) #f
// 第二个宏,用于传递参数给第一个宏进行处理
#define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)int main() {// 使用连接运算符宏int CONCAT(number, 1) = 42; // 等同于 int number1 = 42;printf("Value: %d\n", number1);// 注意:下面这个例子在预处理阶段将连接运算符宏展开为"Hello",而不是一个新的标识符printf("%s\n", "Hello" "World");printf("The value is: %s\n", SHELL_STRINGIFY(number1));//使用双层宏定义return 0;
}

在这里插入图片描述

函数宏

系统函数宏

将系统函数变成我们自己的宏即可省去后期我们大量调用时的麻烦
当然也可以自己写个函数通过宏实现

# define GETPID (int)GetCurrentProcessId//获取当前进程函数
#include <stdio.h>
#include <windows.h>// 定义宏,用于获取当前进程ID
#define GETPID (int)GetCurrentProcessId// 定义宏,用于检查给定的字符是否为空白字符(包括空格、制表符、换行符等)
#define IsSpace(X)  isspace((unsigned char)X)// 定义宏,用于检查给定的字符是否为十进制数字字符(0-9)
#define IsDigit(X)  isdigit((unsigned char)X)// 定义宏,用于将给定的字符转换为小写字符
#define ToLower(X)  (char)tolower((unsigned char)X)// 定义宏,用于将给定的字符转换为大写字符
#define ToUpper(X)  (char)toupper((unsigned char)X)int main() {// 使用宏来获取当前进程IDint pid = GETPID();// 输出当前进程IDprintf("Current process ID: %d\n", pid);// 另外,我们也可以直接调用 GetCurrentProcessId 函数来获取进程IDint pid_direct = GetCurrentProcessId();printf("Current process ID (direct call): %d\n", pid_direct);// 测试字符处理的宏char ch = 'a';// 使用 IsSpace 宏来检查字符 ch 是否为空白字符if (IsSpace(ch)) {printf("The character is a whitespace character.\n");} else {printf("The character is not a whitespace character.\n");}// 使用 IsDigit 宏来检查字符 ch 是否为数字字符if (IsDigit(ch)) {printf("The character is a digit.\n");} else {printf("The character is not a digit.\n");}// 使用 ToLower 宏将字符 ch 转换为小写形式,并输出转换后的字符printf("The lowercase of the character is: %c\n", ToLower(ch));// 使用 ToUpper 宏将字符 ch 转换为大写形式,并输出转换后的字符printf("The uppercase of the character is: %c\n", ToUpper(ch));return 0;
}

在这里插入图片描述

自定义函数宏

//宏定义自己的函数
//注意显示转换不是必须的,但写上更加直观
#include <iostream>
using namespace std;#define MIN(x,y) (int)min((int)x, (int)y)
int min(int a,int b) {int ret = a<b?a:b;cout<<"min is "<<ret<<endl;return ret;
}int main()
{cout<<MIN(10,5);return 0;
}

在这里插入图片描述

多语句执行宏do while0

# define CONTINUE_PROMPT_RESET \do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0)
  • 当有多条语句要执行时需要用{}将函数括起来,而用{}需要用do while循环好处:
    1、使宏的用法更像函数调用,增加了代码的可读性和可维护性
    2、可以避免一些编译器警告,因为使用了多条语句,编译器会认为这是一个复合语句
    3、do-while(0) 循环体会执行一次,因为条件永远为真。这并不会引起运行时额外的开销,因为优化后的编译器会将其优化掉
  • 重点:宏中需要一行写完,如果分行需要用 \ 进行换行
#include <stdio.h>
// 假设这是全局变量,实际情况下可能是其他类型的全局变量
int dynPrompt;// 假设这是虚拟的函数,实际情况下可能是其他具体的函数
void setLexemeOpen(int* prompt, int arg1, int arg2) {// 这里省略函数的具体实现dynPrompt = -1;printf("setLexemeOpen called with arguments: %d, %d\n", arg1, arg2);
}void trackParenLevel(int* prompt, int arg) {// 这里省略函数的具体实现printf("trackParenLevel called with argument: %d\n", arg);
}// 定义宏
#define CONTINUE_PROMPT_RESET \do { \setLexemeOpen(&dynPrompt, 0, 0); \trackParenLevel(&dynPrompt, 0); \} while(0)int main() {printf("Before calling CONTINUE_PROMPT_RESET:\n");printf("dynPrompt = %d\n", dynPrompt);// 使用宏 CONTINUE_PROMPT_RESETCONTINUE_PROMPT_RESET;printf("\nAfter calling CONTINUE_PROMPT_RESET:\n");printf("dynPrompt = %d\n", dynPrompt);return 0;
}

在这里插入图片描述

# define CONTINUE_PROMPT_AWAITS(p,s) \if(p && stdin_is_interactive) setLexemeOpen(p, s, 0)//这个 if 是条件语句(单个条件语句),所以不用 do while0

普通宏定义

  • 宏代表的是指针
    #define CONTINUE_PROMPT_PSTATE_PTR (&dynPrompt)
  • 宏代表的是变量
    #define CONTINUE_PROMPT_PSTATE_VALUE dynPrompt
  • 代表的是具体的常数
    #define CONTINUE_PROMPT_PSTATE_CONSTANT 42
#include <stdio.h>// 假设这是全局变量,实际情况下可能是其他类型的全局变量
int dynPrompt = 42;// 定义三个不同的宏
#define CONTINUE_PROMPT_PSTATE_PTR (&dynPrompt)
#define CONTINUE_PROMPT_PSTATE_VALUE dynPrompt
#define CONTINUE_PROMPT_PSTATE_CONSTANT 42int main() {int *ptr = CONTINUE_PROMPT_PSTATE_PTR; // ptr 指向 dynPrompt 的地址int value = CONTINUE_PROMPT_PSTATE_VALUE; // value 的值是 dynPrompt 的值,即 42int constant = CONTINUE_PROMPT_PSTATE_CONSTANT; // constant 的值是 42printf("dynPrompt: %d\n", dynPrompt);printf("Address of dynPrompt: %p\n", &dynPrompt);printf("ptr: %p\n", ptr);printf("value: %d\n", value);printf("constant: %d\n", constant);return 0;
}

在这里插入图片描述

C的一些必备函数知识

回调函数和函数指针

#include <iostream>
using namespace std;
//函数指针
void (*funPtr)(int a,int b);
//下面的 max和min会赋值给函数指针
void max(int a,int b){int ret = a>b?a:b;cout<<"max is "<<ret<<endl;
}
void min(int a,int b) {int ret = a<b?a:b;cout<<"min is "<<ret<<endl;
}
//回调函数
typedef void (*callBack)(int a,int b);//回调函数比函数指针多了一个typedef
void sameFun(int a, int b , callBack fun){fun(a,b);
}
int main()
{cout << "Hello World" << endl;funPtr = max;funPtr(10,5);funPtr = min;funPtr(10,5);sameFun(10,5,max);sameFun(10,5,min);return 0;
}

在这里插入图片描述

回调函数wireshark-4.0.7源码例子

packet.h:76
typedef int (*dissector_t)(tvbuff_t *, packet_info *, proto_tree *, void *);//回调函数声明packet.c:3310
/* Register a new dissector by name. */
dissector_handle_t
register_dissector(const char *name, dissector_t dissector, const int proto)
{struct dissector_handle *handle;handle = new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, name, NULL, NULL);return register_dissector_handle(name, handle);
}packet-ssh.c:4799ssh_handle = register_dissector("ssh", dissect_ssh, proto_ssh);packet-ssh.c:676static int
dissect_ssh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
/*这里面是ssh解析的具体代码,不做展示*/
}

函数指针wireshark4.0.7源码例子

packet-ssh.c:185
truct ssh_flow_data {guint   version;gchar*  kex;int   (*kex_specific_dissector)(guint8 msg_code, tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *tree,struct ssh_flow_data *global_data, guint *seq_num);//函数指针声明/* [0] is client's, [1] is server's */
#define CLIENT_PEER_DATA 0
#define SERVER_PEER_DATA 1
/*后续代码略*/
}
//---------------------------------------------------------------------------------
packet-ssh.c:1702
static void ssh_set_kex_specific_dissector(struct ssh_flow_data *global_data)
{const char *kex_name = global_data->kex;if (!kex_name) return;if (strcmp(kex_name, "diffie-hellman-group-exchange-sha1") == 0 ||strcmp(kex_name, "diffie-hellman-group-exchange-sha256") == 0){global_data->kex_specific_dissector = ssh_dissect_kex_dh_gex;//函数指针赋值}/*dh group 和 ecdh 略*/
}
//---------------------------------------------------------------------------------
packet-ssh.c:1361
static int ssh_dissect_kex_dh_gex(guint8 msg_code, tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *tree,struct ssh_flow_data *global_data, guint *seq_num)

结构体和关键字

结构体和联合体

结构体 struct

static struct DynaPrompt {char dynamicPrompt[PROMPT_LEN_MAX];char acAwait[2];int inParenLevel;char *zScannerAwaits;
} dynPrompt = { {0}, {0}, 0, 0 };
  • 因为C没有构造函数,不能像cpp一样在构造函数对结构体进行赋初值,因此它赋初值的方法是在结构体定义的尾部赋值

  • 每个成员变量含义:
    dynamicPrompt 数组被初始化为 {0}:即所有元素都是空字符 ‘\0’。
    acAwait 数组被初始化为{0}:即设置为两个空字符 ‘\0’。
    inParenLevel 被初始化为 0:表示括号的层级初始值为 0。
    zScannerAwaits被初始化为 0:它被初始化为 0,即指向空地址。

联合体 union

  • 联合体的所有成员共用同一块内存空间,不同成员在内存中的位置是重叠的。
  • 联合体的大小等于所有成员中占用内存最大的那个成员的大小
  • 在同一时间,只能使用一个成员,存储一个成员的值会覆盖其他成员的值。
#include <stdio.h>
#include <string.h>union Data {int integerData;float floatData;char stringData[20];
} data;int main() {// 存储整数数据data.integerData = 42;printf("Integer Data: %d\n", data.integerData);// 存储浮点数数据data.floatData = 3.14;printf("Float Data: %.2f\n", data.floatData);// 存储字符串数据strcpy(data.stringData, "Hello, Union!");printf("String Data: %s\n", data.stringData);// 访问整数数据(此时整数数据会被覆盖)printf("Integer Data: %d\n", data.integerData);return 0;
}

在这里插入图片描述

关键字

  • static和extern的作用是相反的

static

因为源码中有大量的static函数、结构体等。
静态函数的作用是:

  • 函数隐藏在当前源文件中,对其他部分不可见,实现隐藏实现细节的目的
  • 将函数声明为 static 可以限制其作用域,避免与其他文件中的同名函数冲突
  • 这是一种常见的编程技巧,用于实现模块化的代码结构和增强程序的安全性。

extern

  • 当在一个源文件中使用 extern 来声明函数时,它告诉编译器该函数是在另一个源文件中定义的,而不是在当前文件中定义的
  • 通常,这样的声明会放在头文件中,然后在其他源文件中包含该头文件,以便其他源文件可以访问和调用声明的函数。

C++关键字 explicit

  • 这个关键字加在构造函数前,保证代码安全性
class MyClass {
public:explicit MyClass(int value) {// 构造函数的实现}
};
//如果没有explicit关键字那么构造函数可以隐式调用
int num = 42;
MyClass obj = num; // 隐式调用构造函数,将整数转换为 MyClass 类型
//加上explicit关键字必须显式创建对象
int num = 42;
MyClass obj(num); // 显式调用构造函数,将整数转换为 MyClass 类型

标志位与位操作

& 、 |、<<、>>

1000&1010 = 1000
1000|1010 = 1010
1<<8 = 1*2的八次方(10000000)
256>>8 = 256/2的八次方(
10000000>>8)
可以简单地理解为加减法的关系,本处不做具体解释,可以参考网上其他信息

标志位(|加标志)(&减/检标志)

#include <stdio.h>// 定义标志位的常量
#define FLAG_A (1 << 0) // 00000001
#define FLAG_B (1 << 1) // 00000010
#define FLAG_C (1 << 2) // 00000100int main() {int flags = 0; // 初始标志位为 0// 设置标志位flags |= FLAG_A; // 将 FLAG_A 设置为 1flags |= FLAG_C; // 将 FLAG_C 设置为 1// 检查标志位if (flags & FLAG_A) {printf("标志位 A 已设置\n");} else {printf("标志位 A 未设置\n");}if (flags & FLAG_B) {printf("标志位 B 已设置\n");} else {printf("标志位 B 未设置\n");}if (flags & FLAG_C) {printf("标志位 C 已设置\n");} else {printf("标志位 C 未设置\n");}return 0;
}

过程

  • flags = 00000000
  • FLAG_A = 00000001
    flags = 00000000 | 00000001 = 00000001
  • FLAG_C = 00000100
    flags = 00000001 | 00000100 = 00000101
  • FLAG_A = 00000001
    flags = 00000101 & 00000001 = 00000001 (非零,表示标志位 A 已设置)
  • FLAG_B = 00000010
    flags = 00000101 & 00000010 = 00000000 (为零,表示标志位 B 未设置)
  • FLAG_C = 00000100
    flags = 00000101 & 00000100 = 00000100 (非零,表示标志位 C 已设置)
  • 这样我们通过 | 加标志 再用 & 检查标志,我们就可以通过3bit的数据获得8种标志位情况
  • 实际上我们不必用&检查标志位,因为8种情况我们都是知道的所以直接 == 0111(0x07) 或 0011(0x03)(等6种情况)即可
    注:各种源码这种标志位过于普遍,本处不再贴此类代码

代码习惯

if else

if( dynPrompt.inParenLevel>9 ){shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4);
}else if( dynPrompt.inParenLevel<0 ){shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
}else{shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4);dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
}

if else break

if( nmb !=0 && noc+nmb <= ncmax ){//sss
}else break; 

函数定义

这种函数参数列表是第一次看到
工作中没有遇到过这种写法,但是这种注释确实比较方便,易读性很好
后续会对这种格式进行讨论

static char *shellFakeSchema(sqlite3 *db,            /* The database connection containing the vtab */const char *zSchema,    /* Schema of the database holding the vtab */const char *zName       /* The name of the virtual table */
){
//。。。
}

其他内容后续补充

这个是为了cJSON库源码学习先初步学习的一些知识储备

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/14872.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通讯录的实现(超详细)——C语言(进阶)

目录 一、创建联系人信息&#xff08;结构体&#xff09; 二、创建通讯录&#xff08;结构体&#xff09; 三、define定义常量 四、打印通讯录菜单 五、枚举菜单选项 六、初始化通讯录 七、实现通讯的的功能 7.1 增加加联系人 7.2 显示所有联系人的信息 ​7.3 单独查…

《MySQL45讲》笔记—索引

索引 索引是为了提高数据查询效率&#xff0c;就像书的目录一样。如下图&#xff0c;索引和数据就是位于存储引擎中&#xff1a; 索引常见模型 哈希表 以键值对存储的数据结构。适用于只有等值查询的场景。 有序数组 在等值查询和范围查询场景中性能都特别优秀。但是有…

开放自动化软件的硬件平台

自动化行业的产品主要以嵌入式系统为主&#xff0c;历来对产品硬件的可靠性和性能都提出很高的要求。最典型的产品要数PLC。PLC 要求满足体积小&#xff0c;实时性&#xff0c;可靠性&#xff0c;可扩展性强&#xff0c;环境要求高等特点。它们通常采用工业级高性能嵌入式SoC 实…

Vue 3:玩一下web前端技术(三)

前言 本章内容为VUE工作过程与相关使用讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;二&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;四&#xff09;_Lion Ki…

SpringBoot 注解

SpringBoot SpringBoot&#xff08;SpringBootApplication&#xff09;问题引入我们的工程在引入spring-boot-starter-web依赖的时候&#xff0c;为什么没有指定版本&#xff08;版本锁定&#xff09;spring-boot-starter-web是个啥&#xff0c;为什么引入了它之后&#xff0c;…

【业务功能篇59】Springboot + Spring Security 权限管理 【下篇】

UserDetails接口定义了以下方法&#xff1a; getAuthorities(): 返回用户被授予的权限集合。这个方法返回的是一个集合类型&#xff0c;其中每个元素都是一个GrantedAuthority对象&#xff0c;表示用户被授予的权限。getPassword(): 返回用户的密码。这个方法返回的是一个字符…

springboot编写mp4视频播放接口

简单粗暴方式 直接读取指定文件&#xff0c;用文件流读取视频文件&#xff0c;输出到响应中 GetMapping("/display1/{fileName}")public void displayMp41(HttpServletRequest request, HttpServletResponse response,PathVariable("fileName") String fi…

stm32通过ESP8266接入原子云

1. ESP8266模块需要烧录原子云固件&#xff0c;此原子云固件和正常的ESP8266固件相比添加了ATATKCLDSTA 和 ATATKCLDCLS 这两条指令&#xff1a; 2. 原子云账号注册及设备建立 设备管理-新增设备-ESP8266 新建设备后新建分组&#xff0c;将设备加入到此分组中&#xff1a; 至此…

ICASSP 2023 | Cough Detection Using Millimeter-Wave FMCW Radar

原文链接&#xff1a;https://mp.weixin.qq.com/s?__bizMzg4MjgxMjgyMg&mid2247486540&idx1&sn6ebd9f58e9f08a369904f9c48e12d136&chksmcf51beb5f82637a3c65cf6fa53e8aa136021e35f63a58fdd7154fc486a285ecde8b8521fa499#rd ICASSP 2023 | Cough Detection Usi…

SwipeDelMenuLayout失效:Could not find SwipeDelMenuLayout-V1.3.0.jar

一、问题描述 最近在工作上的项目中接触到SwipeDelMenuLayout这个第三方Android开发库&#xff0c;然后我就根据网上的教程进行配置。这里先说一下我的开发环境&#xff1a;Android Studio版本是android-studio-2020.3.1.24-windows&#xff0c;gradle版本是7.0.2。 首先是在se…

RWEQ模型——土壤风蚀模拟

详情点击链接&#xff1a;基于“RWEQ”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的实践应用及SCI论文撰写 前沿 土壤风蚀是一个全球性的环境问题。中国是世界上受土壤风蚀危害最严重的国家之一&#xff0c;土壤风蚀是中国干旱、半干旱及部分湿润地区土地荒漠化的…

【Docker】Docker应用部署之Docker容器安装Tomcat

目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search tomcat 二、拉取镜像 docker pull tomcat:版本 三、创建容器 首先在宿主机创建数据卷的目录 mkdir /root/tomcat # 创建目录 cd /root/tomcat # 进入目录 docker run -id -…

前端框架学习-Vue(二)

最近在学习Vue框架&#xff0c;Vue中的内容很多。相当于把之前后端的MVC&#xff0c;V层转移到前端来编写和部署。下面是学习Vue时的大纲。 Vue生命周期是Vue应用的生命周期Vue脚手架&#xff0c;即vue-cli&#xff0c;使用node.js 来创建和启动vue项目Vue组件知识&#xff0c;…

RS485/RS232自由转ETHERNET/IP网关profinet和ethernet区别

你是否曾经遇到过这样的问题&#xff1a;如何将ETHERNET/IP网络和RS485/RS232总线连接起来呢&#xff1f;捷米的JM-EIP-RS485/232通讯网关&#xff0c;自主研发的ETHERNET/IP从站功能&#xff0c;完美解决了这个难题。这款网关不仅可以将ETHERNET/IP网络和RS485/RS232总线连接起…

Zynq-Linux移植学习笔记之62- PL挂载复旦微flash

1、背景介绍 现在为了全国产化需要&#xff0c;之前所有的进口flash全部要换成国产flash 2、复旦微flash型号 其中EFM25QU256和EFM25QL256对标winbond的w25q256 nor flash 3、FPGA设置 复旦微flash只支持单线模式&#xff0c;当使用PL侧的IP核访问时&#xff0c;需要设置模式…

【系统架构】分布式系统架构设计

1 分布式系统是什么 分布式系统是指由多个计算机节点组成的一个系统&#xff0c;这些节点通过网络互相连接&#xff0c;并协同工作完成某个任务。 与单个计算机相比&#xff0c;分布式系统具有更高的可扩展性、可靠性和性能等优势&#xff0c;因此广泛应用于大规模数据处理、高…

IDEA代码自动格式化工具

1.自动import 在IDEA中&#xff0c;打开 IDEA 的设置&#xff0c;找到 Editor -> General -> Auto Import。勾选上 Add unambiguous imports on the flyOptimize imports on the fly (for current project) 2.gitee 提交格式化 设置方法如下: 1.打开设置 2.找到版本…

KMP算法总结

KMP算法总结 BF算法引导BF算法步骤&#xff08;图片演示&#xff09;代码演示 KMP算法推next数组代码演示 BF算法引导 BF算法是一个暴力的字符串匹配算法&#xff0c;时间复杂度是o&#xff08;m*n&#xff09; 假设主串和子串分别为 我们想要找到子串在主串的位置 BF算法核…

Spring Boot集成Swagger3.0,Knife4j导出文档

文章目录 Spring Boot集成Swagger3.0,Knife4j导出文档效果展示如何使用简要说明添加依赖添加配置类测试接口token配置位置 官网 说明情况 demo Spring Boot集成Swagger3.0,Knife4j导出文档 效果展示 如何使用 简要说明 Knife4j的前身是swagger-bootstrap-ui,前身swagger-boo…

Github-Copilot初体验-Pycharm插件的安装与测试

引言&#xff1a; 80%代码秒生成&#xff01;AI神器Copilot大升级 最近copilot又在众多独角兽公司的合力下&#xff0c;取得了重大升级。GitHub Copilot发布还不到两年&#xff0c; 就已经为100多万的开发者&#xff0c;编写了46%的代码&#xff0c;并提高了55%的编码速度。 …