关于MCU产品开发参数存储的几种方案

news/2024/5/17 14:03:40

关于MCU产品开发参数存储的几种方案

  • Chapter1 关于MCU产品开发参数存储的几种方案
  • Chapter2 单片机参数处理[保存与读取]
  • Chapter3 嵌入式设备参数存储技巧
  • Chapter4 STM32硬件I2C的一点心得(AT24C32C和AT24C64C)


Chapter1 关于MCU产品开发参数存储的几种方案

原文链接

在工作中,几乎所有参与产品开发的产品都将实现参数存储功能。

通常,参数存储将使用以下存储介质,例如:eeprom,spi闪存,nand闪存,SD卡等,至于如何存储,现在有很多种类。

1.使用eeprom(以at24c02为例)定义结构,然后定义两个结构变量,一个用于读取参数,一个用于立即写入修改的参数。

参考:2.使用spi_flash(以w25q64为例)方法1与使用eeprom方法相同。

方法2使用文件系统并创建一个ini文件来获取参数。

Chapter2 单片机参数处理[保存与读取]

原文链接:https://blog.csdn.net/WangSanHuai2010/article/details/6988583

/*------------------------------------------------------------Func: 加载参数到系统Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
void WFS_LoadParams(uint16 Addr,uint16 *Buffer,uint16 Length)
{Addr<<=1;Length<<=1;EEPROM_Read(Addr+2,(uint8 *)Buffer,Length);
}

参数按以上方法加载到内存,注意参数的起始地址为2,这是因为前两个字节区域要用来做校验用。

/*------------------------------------------------------------Func: 保存参数Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
void WFS_SaveParams(uint16 Addr,uint16 *Buffer,uint16 Length)
{Addr<<=1;Length<<=1;EEPROM_Write(Addr+2,(uint8 *)Buffer,Length);
}

以上方法保存参数到EEPROM中,实际上与Load方法一一对应。

/*------------------------------------------------------------Func: 参数系统初始化Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
uint8 WFS_InitParams(void *DefaultValues,uint16 Length)
{uint16 D;EEPROM_Read(0,(uint8 *)(&D),2);if(D!=0x55AA){D=0x55AA;EEPROM_Write(0,(uint8 *)(&D),2);EEPROM_Write(2,(uint8 *)DefaultValues,Length);return 0xFF;}return 0x00;
}

参数的初始化方法,首先读取EEPROM的0位置处的数据,判断是否为0x55AA合法标志,若不是0x55AA,则说明参数区为首次使用,需要进行初始化默认参数填充,于是将DefaultValues所指的默认值填入EEPROM中,并设置0x55AA标志,以后每次上电便会检测到参数的合法性。

以下为使用示例,存储了地址码,波特率,数据位,停止位四个参数,以及一个18字的数组。

const uint16 WFS_ParmasValue_Default[]=
{1,9600,8,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

以下为参数进行初始化并加载到内存:


WFS_InitParams(WFS_ParmasValue_Default,sizeof(WFS_ParmasValue_Default));
WFS_LoadParams(0,&DevAddr,1);
WFS_LoadParams(1,&BaudRate,1);
WFS_LoadParams(2,&DataLength,1);
WFS_LoadParams(3,&StopBits,1);
WFS_LoadParams(4,Array,18);

以下为参数修改后进行保存:

BaudRate=115200;
StopBits=2;
WFS_SaveParams(1,&BaudRate,1);
WFS_LoadParams(3,&StopBits,1);

Chapter3 嵌入式设备参数存储技巧

原文链接

1、前言
想必做嵌入式产品开发都遇到过设备需要保存参数,常用的方式就是按照结构体的方式管理参数,保存时将整个结构体数据保存在 Flash 中,方便下次读取。

1.1、目的
本文时分析嵌入式/单片机中参数保存的几种方式的优点和缺点(仅针对单片机/嵌入式开发而言),同时针对以结构体的方式解决一些弊端问题(重点在第 3 节)。

2、参数保存格式
2.1、结构体格式
该方式是嵌入式/单片机中开发最常用的,将所有的系统参数通过结构体的方式定义,然后保存数据,介绍一下该方式的优缺点。

储存方式:二进制 bin 文件格式
1
优点:

管理简单:无需额外的代码直接就能很方便的管理参数
内存最小:通过结构体的形式保存在Flash中,占用内存最小
缺点:

1.扩展性差:

从产品角度来说,产品需要升级,若是涉及增加参数,则升级后参数通常无法校验通过(通常包含长度校验等),导致参数被恢复默认
若是每个模块都存在自己的独有结构体参数定义,删除/新增时势必影响到其他的,导致设备升级后参数错乱(结构体中的变量地址在 bin 文件中是固定的)
2.阅读性差:
若参数需要导出,bin文件没有可读性

改进措施:
结构体增加预留定义,若之后需要新增参数,则在预留空间新增即可,能在一定程度上解决扩展性差的问题,即新增不影响原有的结构体大小和其他成员变量的位置,删除恢复成预留即可。

为啥说只能在一定程度上解决该问题,因为之后的升级某些模块可能很长时间或者从不需要增加新的参数,这种势必就会造成内存的无效占用,或者有些模块频繁增加参数导致预留大小不够等问题,只能在前期设计时多加思考预留的分配情况(毕竟内存只有那么大)

/*****************************改进之前
*****************************/typedef struct
{uint8_t testParam;uint8_t testParam2;
} TestParam_t;    /* 某模块参数 */typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;
} SystemParam_t; /* 系统参数 *//*****************************改进之后
*****************************/typedef struct
{uint8_t testParam;uint8_t testParam2;uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;uint8_t reserve[50];   // 预留
} SystemParam_t; /* 系统参数 */

2.2、JSON格式
最近Json格式很是流行使用,特别是数据交换中用的很多,但是它也可以用来保存参数使用,JSON 的是 “{键:值}” 的方式。

储存方式:字符串格式,即文本的形式
1
优点:

扩展性好:由于Json的格式,找到对应键值(一般都是该变量的标识),就能找到对应的值
阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂
缺点:

管理相对复杂:没有结构体那么简单,不熟还得先学习 JSON 的写法
内存占用较大:内容不只有值,而且都按照字符串的形式保存的
使用相关困难:需要解析,C语言虽然有开源库,但是由于语言性质使用不方便,C++ 反而使用简单

{"SYS":{"testParam" : 2,"testParam2" : 5,"tTestParam":{"testParam" : 2,"testParam2" : 5}}
}//压缩字符串为:
{"SYS":{"testParam":2,"testParam2":5,"tTestParam":{"testParam":2,"testParam2":5}}}

2.3、键值格式
和上述的 JSON 格式很类似,都是键值对的格式,但是比JSON简单

储存方式:字符串格式,即文本的形式
1
优点:

扩展性好:找到对应键值(一般都是该变量的标识),就能找到对应的值
阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂
缺点:

内存占用较大:内容不只有值,而且都按照字符串的形式保存的
使用稍微困难:需要简单解析处理
管理不变:不方便按照一定的规则管理各模块的参数

testParam=2
testParam2=5
T_testParam=2
T_testParam2=5

2.4 其他
还有其他,如 xml (类似JSON)等,就不多介绍了

3、编译器检查结构体的大小和成员变量的偏移
在第 2 节中介绍了关于参数保存的三种方式,但是对于嵌入式单片机开发而言,Flash 大小不富裕,所以通常都是通过二进制的形式保存的,所以这节重点解决结构体管理保存参数的扩展性问题。

先说一下痛点(虽然对扩展性问题做了改进措施,除了前面讲到的问题,还有其他痛点,虽不算问题,但是一旦出现往往最要命)

在原来的预留空间中新增参数,要确保新增后结构体的大小不变,否则会导致后面的其他参数偏移,最后升级设备后参数出现异常(如果客户升级那就是要命啊)
确保第一点,就必须在每次新增参数都要计算检查一下结构体的大小有没有发生变化,而且有没有对结构体中的其他成员也产生影响
每次新增参数,手动计算和校验 99% 可以检查出来,但是人总有粗心的时候(加班多了,状态不好…),且结构体存在填充,一不留神就以为没问题,提交代码,出版本(测试不一定能发现),给客户,升级后异常,客户投诉、扣工资(难啊…)
遇到这种问题后:难道编译器就不能在编译的时候检查这个大小或者结构体成员的偏移吗,每次手动计算校验好麻烦啊,一不留神还容易算错 # _ #

按照正常情况,编译器可不知道你写的结构体大小和你想要的多大,所以检查不出来(天啊,崩溃了0.0…)

别急,有另类的方式可以达到这种功能,在编译时让编译器为你检查,而且准确性 100%(当然,这个添加新参数时你还得简单根据新增的参数大小减少预留的大小,这个是必须要的)

见代码:

/*** @brief 检查结构体大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param size 结构体检查大小*/
#define TYPE_CHECK_SIZE(type, size) extern int sizeof_##type##_is_error [!!(sizeof(type)==(size_t)(size)) - 1]/*** @brief 结构体成员* @param type   结构体类型* @param member 成员变量*/
#define TYPE_MEMBER(type, member) (((type *)0)->member)/*** @brief 检查结构体成员大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体类型* @param size 结构体检查大小*/
#define TYPE_MEMBER_CHECK_SIZE(type, member, size) extern int sizeof_##type##_##member##_is_error \[!!(sizeof(TYPE_MEMBER(type, member))==(size_t)(size)) - 1]/*** @brief 检查结构体中结构体成员大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体类型* @param size 结构体检查大小*/
#define TYPE_CHILDTYPE_MEMBER_CHECK_SIZE(type, childtype, member, size) extern int sizeof_##type##_##childtype##_##member##_is_error \[!!(sizeof(TYPE_MEMBER(type, childtype.member))==(size_t)(size)) - 1]/*** @brief 检查结构体成员偏移位置是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体成员* @param value 成员偏移*/
#define TYPE_MEMBER_CHECK_OFFSET(type, member, value) \extern int offset_of_##member##_in_##type##_is_error \[!!(__builtin_offsetof(type, member)==((size_t)(value))) - 1]/*** @brief 检查结构体成员偏移位置是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体成员* @param value 成员偏移*/
#define TYPE_CHILDTYPE_MEMBER_CHECK_OFFSET(type, childtype, member, value) \extern int offset_of_##member##_in_##type##_##childtype##_is_error \[!!(__builtin_offsetof(type, childtype.member)==((size_t)(value))) - 1]

通过以上代码,就能解决这个问题,这个写法只占用文本大小,编译后不占内存!!!

用法:

typedef struct
{uint8_t testParam;uint8_t testParam2;uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */TYPE_CHECK_SIZE(TestParam_t, 8); // 检查结构体的大小是否符合预期typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;uint8_t reserve[54];   // 预留
} SystemParam_t; /* 系统参数 */TYPE_CHECK_SIZE(SystemParam_t, 64); // 检查结构体的大小是否符合预期
TYPE_MEMBER_CHECK_OFFSET(SystemParam_t, tTestParam, 2); // 检查结构体成员tTestParam偏移是否符合预期

假设新增了参数,预留写错了,导致结构体的大小不符合,则编译时报错,且提示内容也能快速定位问题。
在这里插入图片描述

Chapter4 STM32硬件I2C的一点心得(AT24C32C和AT24C64C)

原文链接:https://blog.csdn.net/whitefish520/article/details/110070972

从设备读写
一般的EEPROM,像AT24C02这种小容量的设备,地址都只需要8位,页大小一般是16字节一个页
而像AT24C32C、AT24C64C这种32K、64K字节的大容量EEPROM,8位地址是不够的,使用了16位地址,页大小在这两个器件中也变成了32字节
正是由于容量的不同,导致代码上需要做差异化处理,才能正确读取EEPROM芯片

以下代码可以参考,写的时候无法跨页,因此写大量数据的时候,只能一页一页的写,两次写之间保证5MS的间隔
读没有跨页的影响,可以一次性把全部数据读出来,但是要注意,读和写之间,是要有5MS的间隔的,否则读不到数据。也就是说每次写完,延迟5MS,就能保证后续的程序没有问题。

//#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_8BIT			//小容量EEPROM芯片8位地址用此参数
#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_16BIT		//大容量EEPROM芯片16位地址用此参数
#define ADDR_AT24C02_Write 0xA0		//EEPROM I2C写地址
#define ADDR_AT24C02_Read 0xA0+1	//EEPROM I2C读地址typedef enum SYS_PARA_ENUM {LOCAL_IP = 0,UDP_LOCAL_PORT,UDP_PC_PORT,NETMASK,GATEWAY,SERVER_IP,SERVER_PORT,VERSION,SN_NUM,	//16字节SENSOR_TYPE = SN_NUM + 4,SENSOR_DATA_TYPE,SENSOR_INTERVAL,SYS_PARA_MAX,		//end
}sys_para_e;//请注意,为了方便flash读写操作,此处的每一项均设为uint32_t类型
//如果不为uint32_t类型,则flash_write函数将出现错误
typedef struct SYS_PARA_TYPE {uint32_t local_ip;			//本机IP,大端模式uint32_t udp_local_port;	//本机端口号uint32_t udp_pc_port;		//PC端口号uint32_t netmask;			//本机子网掩码,大端模式uint32_t gateway;			//本机网关,大端模式uint32_t server_ip;			//服务器IP,大端模式uint32_t server_port;		//服务器端口号uint32_t version;			//stm32软件版本号uint32_t sn_num[4];			//SN号	5 6 7 8uint32_t sensor_type;		//传感器类型 9uint32_t sensor_data_type;	//传感器数据类型uint32_t sensor_interval;	//传感器采集时间间隔
}sys_para_t;/* -----------------------------------------------------------------------------
函数名:  	i2c_write
作者:    	glx
日期:    	2020-11-10
功能:    	数据写入eeprom
输入参数:	pData:数据指针
返回值:  	类型:HAL_StatusTypeDefHAL_OK:操作成功HAL_ERROR:操作失败
修改记录:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_write(void *pData)
{HAL_StatusTypeDef ret = HAL_ERROR;uint8_t i, page, pageSize;uint8_t *p = (uint8_t *)pData;if(pData != NULL){if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_8BIT){pageSize = 16;			//一页16字节,不能跨页写page = SYS_PARA_MAX/4;}else if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_16BIT){pageSize = 32;			//一页32字节,不能跨页写page = SYS_PARA_MAX/8;}//写完整的页for(i = 0; i<page; i++){ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, pageSize, 100);p += pageSize;if(ret != HAL_OK){printf("I2C_Write Sys Para write error\r\n");return HAL_ERROR;}HAL_Delay(5);}//写残缺的页if(SYS_PARA_MAX > (page*4))	{ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, 4*SYS_PARA_MAX-pageSize*page, 100);if(ret != HAL_OK){printf("I2C_Write Sys Para write error\r\n");return HAL_ERROR;}HAL_Delay(5);}}else{printf("I2C_Write pData NULL\r\n");return HAL_ERROR;}return HAL_OK;
}/* -----------------------------------------------------------------------------
函数名:  	i2c_read
作者:    	glx
日期:    	2020-11-10
功能:    	读取eeprom数据
输入参数:	pData:数据指针
返回值:  	类型:HAL_StatusTypeDefHAL_OK:操作成功HAL_ERROR:操作失败
修改记录:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_read(void *pData)
{HAL_StatusTypeDef ret = HAL_ERROR;if(pData != NULL){ret = HAL_I2C_Mem_Read(&hi2c1, ADDR_AT24C02_Read, 0, I2C_MEMADD_SIZE, (uint8_t *)pData, SYS_PARA_MAX*4, 100);if(ret != HAL_OK){printf("I2C_Read Sys Para read error\r\n");return HAL_ERROR;}		}else{printf("I2C_Read pData NULL\r\n");return HAL_ERROR;}return HAL_OK;
}

读写的代码


//用于保存系统参数
sys_para_t sys_para = {0};	
//用于保存系统默认参数
sys_para_t default_para = {.local_ip = 107<<24 | 10<<16 | 168<<8 | 192,.udp_local_port = 18080,.udp_pc_port = 18081,.netmask = 0<<24 | 255<<16 | 255<<8 | 255,.gateway = 1<<24 | 10<<16 | 168<<8 | 192,.server_ip = 9<<24 | 10<<16 | 168<<8 | 192,.server_port = 18082,.version = 20201124,.sn_num[0] = 0xffffffff,.sn_num[1] = 0xffffffff,.sn_num[2] = 0xffffffff,.sn_num[3] = 0xffffffff,.sensor_type = SENSOR_DOOR,.sensor_data_type = SENSOR_DATA_GPIO,.sensor_interval = 1000,
};i2c_read(&sys_para);
i2c_write(&default_para);

http://www.mrgr.cn/p/27415233

相关文章

c++四则运算结对编程 2252416 黄子轩

一、实验内容 本次课程内容为结对编程。与我组队的同学学号为2252428. 本次结对编程的题目是小学老师要每周给同学出300道四则运算练习题。 两个运算符,100 以内的数字,不需要写答案。 需要检查答案是否正确,并且保证答案在 0..100 之间 我们使用的是c++代码 二、代码 代码如…

python 爬虫 下载百度美女图片

因为要从网上下载很多图片,一张一张的复制下载速度很慢。爬虫实现方式查找到访问图片的链接URI 访问URI获取到图片的链接 访问图片的链接,并保存图片到本地废话不多说 上代码 import requests import jsondef get_image_url():url = https://image.baidu.com/search/index?…

Linux:Redis7.2.4的简单在线部署(1)

注意&#xff1a;我写的这个文章是以最快速的办法去搭建一个redis的基础环境&#xff0c;作用是为了做实验简单的练习&#xff0c;如果你想搭建一个相对稳定的redis去使用&#xff0c;可以看我下面这个文章 Linux&#xff1a;Redis7.2.4的源码包部署&#xff08;2&#xff09;-…

李沐45_SSD实现——自学笔记

主体思路&#xff1a; 1.生成一堆锚框 2.根据真实标签为每个锚框打标(类别、偏移、mask) 3.模型为每个锚框做一个预测(类别、偏移) 4.计算上述二者的差异损失&#xff0c;以更新模型weights 先读取一张图像。 它的高度和宽度分别为561和728像素。 %matplotlib inline import …

IO基础-IO多路复用基础

Java的Selector封装了底层epoll和poll的API&#xff0c;可以通过指定如下参数来调用执行的内核调用, 在Linux平台&#xff0c;如果指定 -Djava.nio.channels.spi.SelectorProvidersun.nio.ch.PollSelectorProvider 则底层调用poll&#xff0c; -Djava.nio.channels.spi.Selec…

HarmonyOS实战开发-自定义通知角标、如何设定应用的桌面图标角标的功能。

介绍 本示例主要展示了设定应用的桌面图标角标的功能&#xff0c;使用ohos.notificationManager 接口&#xff0c;进行桌面角标的设置&#xff0c;通知的发送&#xff0c;获取等。 效果预览 使用说明 在使用本应用时&#xff0c;需安装并启动仿桌面应用&#xff1b;在主界面…

Linux系统一键安装DataEase结合内网穿透实现公网访问本地WebUI界面

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

GPT国内怎么用

2022年11月&#xff0c;OpenAI发布了ChatGPT&#xff0c;这标志着大型语言模型在自然语言处理领域迈出了巨大的一步。ChatGPT不仅在生成文本方面表现出了惊人的流畅度和连贯性&#xff0c;更为人工智能应用开启了全新的可能性。 ChatGPT的推出促进了人工智能技术在多个领域的广…

【GUI软件】小红书按关键词采集笔记详情,支持多个关键词,含笔记正文、转评赞藏等,爬了1024w条!

小红书采集软件,根据关键词自动采集笔记详情。一、背景介绍 1.1 爬取目标 熟悉我的小伙伴都了解,我之前开发过2款软件:【GUI软件】小红书搜索结果批量采集,支持多个关键词同时抓取! 【GUI软件】小红书详情数据批量采集,含笔记内容、转评赞藏等,支持多笔记同时采集!现在…

提升法律文书起草效率:AlphaGPT 助力律师快速生成诉讼和仲裁文件

法律文书起草对于法律专业人士而言是一项基础而关键的任务。无论是民事、刑事还是行政诉讼&#xff0c;以及仲裁案件&#xff0c;精确的法律文书撰写对于案件的成功至关重要。然而&#xff0c;这一过程往往既耗时又复杂&#xff0c;尤其是在处理复杂的案情和面对当事人难以理解…

图片批量高效管理,支持批量转换图片格式并按比例进行放大150%,高效掌握图片

在这个数字化时代&#xff0c;图片已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;面对海量的图片文件&#xff0c;如何高效地进行管理、编辑和优化&#xff0c;成为了摆在我们面前的一大难题。现在&#xff0c;一款全新的图片批量管理工具来了&#xff0c;它能够…

进程控制

linux下进程控制,等待,退出,fork()函数,fork返回id的细节目录1.fork函数1.1如何理解fork函数有两个返回值问题1.2如何理解fork给父进程返回自己的pid,自己返回01.3如何理解同一个id值,有两个不同的值2.进程终止2.1进程退出码2.2进程退出2.2.1进程常见退出办法2.2.3exit()…

React脚手架的搭建与使用

React脚手架是开发现代Web应用的必备&#xff0c;其充分利用Webpack、Babel、ESlint等工具辅助项目的开发&#xff0c;当然这些工具也无需手动配置即可使用&#xff0c;脚手架的意义更多的是关注的是业务而不是工具的配置&#xff1b;项目的整体技术架构为&#xff1a;react w…

JS 中 reduce()方法及使用详解

reduce()方法可以搞定的东西特别多,就是循环遍历能做的,reduce都可以做,比如数组求和、数组求积、统计数组中元素出现的次数、数组去重等等。 reduce() 方法对数组中的每个元素执行一个由您提供的reduce函数(依次执行),将其结果汇总为单个返回值。 1、语法介绍// arr.reduc…

探索Java世界中的七大排序算法(上)

文章目录 排序的概念直接插入排序希尔排序( 缩小增量排序)选择排序堆排序冒泡排序 在计算机科学中&#xff0c;排序算法是一类重要的算法&#xff0c;它们用于将一组元素按照一定的顺序进行排列。在Java编程中&#xff0c;我们经常需要对数组或集合进行排序操作。本文将介绍Jav…

高并发(AQS)

AQS 抽象的队列同步器框架,主要通过程序来构建锁和同步器AQS 抽象的队列同步器框架,主要通过程序来构建锁和同步器 AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是抽象队列同步器,它和Java的Synchronized作用和一样,用来同步加锁; 特性对比ReentrantLock …

JAVA基础-流程控制、字符串

一、java基础 1、java主类结构 package com.study.again001; 包名public class helloword { 类名 static String s1 = "1"; 静态成员变量 public static void main(String[] args) { main方法 String s2 = "2"; 局部变量 …

C到二进制概述

此帖是记录视频内容,防止后续自己遗忘 一、预处理 1、库文件 预处理等于一个文本粘贴过程,编译后把库文件展开,展开本质也为粘贴内容include 本质是在其中特定路径搜索(usr/include...),而#include "xxx"是在当前路径下进行搜索 。其中#include" "高于…

Qt6连接MySQL

Qt6连接MySQL Qt6编译MySQL的过程太变态了&#xff0c;MinGW和MSVC都很费劲。 主要参考qt6.5.0MySQL驱动手动编译以及数据库连接详细教程以及注意事项附资源链接&#xff0c;这篇文章堪称保姆级教程&#xff0c;写的十分详细。 笔者这里踩了个坑&#xff0c;在按照上文中的过…

AliyunCTF 2024 - BadApple

文章目录 前言环境搭建漏洞分析漏洞利用参考 前言 本文首发于看雪论坛 https://bbs.kanxue.com/thread-281291.htm 依稀记得那晚被阿里CTF支配的恐惧&#xff0c;今年的阿里CTF笔者就做了一道签到PWN题&#xff0c;当时也是下定决心要学习 jsc pwn 然后复现这道 BadApple 题目…