RTOS队列的写入与读出

      我们在stm32f103c8t6单片机上验证RTOS队列的写入与读出,利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟,裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以需要需要为其他总线提供另外的时钟源。

验证的功能比较简单,选择V1 版本的内核完全够用。

一、验证的思路及需用函数及队列使用的说明

1.创建2个任务taskKEY1,taskKEY2。

任务要求如下:

taskKEY1:按下KEY1调用xQueueSend()函数,发送数据。发送成功,显示发送的数据;发送失败,显示写入队列失败。

taskKEY2:按下KEY2调用xQueueReceive()函数,读取数据。读取成功,显示读取的数据;读取失败,显示读取队列失败。

2.需要的函数

创建队列

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

uxQueueLength:队列可同时容纳的最大项目数 。

uxItemSize:存储队列中的每个数据项所需的大小(以字节为单位)。 返回值: 如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法分配 , 则返回 NULL。

写队列

BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

xQueue:队列的句柄,数据项将发送到此队列

pvItemToQueue:待写入数据

xTicksToWait:阻塞超时时间

如果成功写入数据,返回 pdTRUE,否则返回 errQUEUE_FULL。

该函数会往队列的尾部写入消息,当你写入的数据超过了队列长度或者在阻塞时间内没有写入数据就会写入失败

读队列

BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait )

xQueue:待读取的队列

pvItemToQueue:数据读取缓冲区

xTicksToWait:阻塞超时时间

返回值: 成功返回 pdTRUE,否则返回 pdFALSE。

该函数会从队列头部读取消息,并删除消息,当你读出的的数据超过了写入的数据或者在阻塞时间内没有读出数据就会读出失败

3. 队列使用的说明

  队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息。

队列项目:队列中的每一个数据;(打个预防针,每一个的理解不是真的就一个,这篇文章的代码部分freertos.c那里我会说明。)

队列长度:队列能够存储队列项目的最大数量;

创建队列时,需要指定队列长度及队列项目大小。这个在我们使用stm32cube配置的时候会提到。

队列的特点

①通常采用先进先出(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取。 也可以配置为后进先出(LIFO)方式,但用得比较少。FIFO就是First Input First Output

②采用实际值传递,即将数据拷贝到队列中进行传递,也可以传递指针,在传递较大的数据的时候 采用指针传递。我们的读写队列函数里面的数据参数就是指针类型。

③队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息

④当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。 阻塞时间如果设置为: 0:直接返回不会等待;

0~port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不 再等待;

port_MAX_DELAY:死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;

二、stm32cube的配置

SYS

RCC

GPIO

PA0对应按键1,PA1对应按键2。

RTOS

在Tasks and Queues中配置我们的2个任务,一个队列

其实就是相当于stm32cube帮我们调用了并封装了生成任务xTaskCreate(),生成队列xQueueCreate函数。

两个任务和一个队列的名字分别是

TaskSend,TaskReceive, myQueue

两个任务的入口函数名字分别是

StartSend,StartReceive;

各参数的配置相同,如下图

任务1和2除了任务名字和入口函数不同,其余的都相同

三、代码部分

usart.c

#include "stdio.h"

int fputc(int ch, FILE *f)

{

unsigned char temp[1]={ch};

HAL_UART_Transmit(&huart1,temp,1,0xffff);

return ch;

}

同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。

在freertos.c里我要分两种情况给大家说明创建队列的时候队列项目和队列长度

队列项目:队列中的每一个数据;

队列长度:队列能够存储队列项目的最大数量;

freertos.c

首先来验证一次性发送一个数据,读一个数据

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "stdio.h"

//#include "queue.h"

//QueueHandle_t queue;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskSendHandle;

osThreadId TaskReceiveHandle;

osMessageQId myQueueHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartSend(void const * argument);

void StartReceive(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */

  /* definition and creation of myQueue */

  osMessageQDef(myQueue, 16, uint16_t);

  myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

// queue = xQueueCreate(5,12);

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskSend */

  osThreadDef(TaskSend, StartSend, osPriorityNormal, 0, 128);

  TaskSendHandle = osThreadCreate(osThread(TaskSend), NULL);

  /* definition and creation of TaskReceive */

  osThreadDef(TaskReceive, StartReceive, osPriorityNormal, 0, 128);

  TaskReceiveHandle = osThreadCreate(osThread(TaskReceive), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartSend */

/**

  * @brief  Function implementing the TaskSend thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartSend */

void StartSend(void const * argument)

{

  /* USER CODE BEGIN StartSend */

      

       uint16_t buf = 100;

BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

       status = xQueueSend(myQueueHandle, &buf, 0);

       if (status == pdTRUE)

       printf("写入队列成功,写入值为%d\r\n", buf);

       else

       printf("写入队列失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

       }

       osDelay(10);

              }

  /* USER CODE END StartSend */

}

/* USER CODE BEGIN Header_StartReceive */

/**

* @brief Function implementing the TaskReceive thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartReceive */

void StartReceive(void const * argument)

{

  /* USER CODE BEGIN StartReceive */

      

       uint16_t buf;

BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

                     if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)

       {

       status = xQueueReceive(myQueueHandle, &buf, 0);

       if (status == pdTRUE)

       printf("队列数据读取成功,读出值为%d\r\n", buf);

       else

       printf("队列读取失败\r\n");

       }

       while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);

       }

       osDelay(10);

  }

  /* USER CODE END StartReceive */

}

需要说明的是

osMessageQDef(myQueue, 16, uint16_t);

myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

这两个函数是stm32cube封装过的队伍生成函数xQueueCreate(),第一个函数的参数是队伍的名字,队列的长度,一个数的的大小;第二个是返回队伍的句柄,也就是myQueueHandle,我们在xQueueSend和xQueueReceive都要用到该句柄。

我们可以看到当独处的数据超过写入的数据,读出就会失败

freertos.c

再来验证一次性发送多个数据,读多个数据

include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "queue.h"

QueueHandle_t queue;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskSendHandle;

osThreadId TaskReceiveHandle;

osMessageQId myQueueHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartSend(void const * argument);

void StartReceive(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */

  /* definition and creation of myQueue */

  osMessageQDef(myQueue, 5, 12);

  myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

       queue = xQueueCreate(5,12);

  /* USER CODE BEGIN RTOS_QUEES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskSend */

  osThreadDef(TaskSend, StartSend, osPriorityNormal, 0, 128);

  TaskSendHandle = osThreadCreate(osThread(TaskSend), NULL);

  /* definition and creation of TaskReceive */

  osThreadDef(TaskReceive, StartReceive, osPriorityNormal, 0, 128);

  TaskReceiveHandle = osThreadCreate(osThread(TaskReceive), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartSend */

/**

  * @brief  Function implementing the TaskSend thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartSend */

void StartSend(void const * argument)

{

  /* USER CODE BEGIN StartSend */

      

       int buf[3] = {100,200,300};

       BaseType_t status;

  /* Infinite loop */

  for(;;)

  {

             

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

{

       osDelay(20);

       if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)

       {

              status = xQueueSend((QueueHandle_t)queue, buf,500);

              if (status == pdTRUE)

               printf("写入队列成功,写入值为%d,%d,%d\r\n",buf[0],buf[1],buf[2] );

             

              else

               printf("写入队列失败\r\n");

}

while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);

}

osDelay(10);

  }

  /* USER CODE END StartSend */

}

我们可以看到,一次性读写三个数据

一次性读写一个数字与一次性读写三个数字的区别

基于一次性读写一个数字的基础上,我们在开头加入了

#include "queue.h"

QueueHandle_t queue;

在MX_FREERTOS_Init()里面有额外加了queue = xQueueCreate(5,12);

这一段程序的作用就相当于把原来我们在stm32cube里面的对创建任务的参数,又重新配置了一遍。原来的参数是队列长度是16,大小是uint16_t,我们在StartSend和StartReceive传递的参数大小也是uint16_t,所以一次只能传输一个。现在我们把队列的长度改成了5,大小是12字节;我们在StartSend和StartReceive传递的参数大小也是一个int,4个字节,刚好一次性可以传输3个,程序里面细微的才别我就不说了,自己去看看,很容易找的。

所以队列中的每一个数据,一个数据不止只传输一个数字,而是传输一个和你规定队伍参数一样大小的数据。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGLM基于LangChain应用开发实践(一)

一、概述 在使用大模型(LLM)做应用开发时,LangChain是一个主流的开发框架,通过它来构建Agent,根据用户查询访问企业私有数据,调用自定义或者第三方工具库,然后再调用LLM,利用其推理…

qt-C++笔记之std::tostring()、.toStdString()、.toLocal8Bit().constData()的使用场景

qt-C笔记之std::tostring()、.toStdString()、.toLocal8Bit().constData()的使用场景 参考博文:C笔记之system()用于在Qt中执行系统命令的习惯 code review! 注:之所以记录该笔记,是因为在Qt中自己经常使用C语言的int system( const char …

c++11--左值,右值,移动语义,引用折叠,模板类型推断,完美转发

1.移动语义 移动构造和移动赋值均属于移动语义范畴。 移动语义的实现依赖于右值概念&#xff0c;右值引用。 1.1.一个移动构造的实例 #include <iostream> using namespace std; class HasPtrMem{ public:HasPtrMem():d(new int(3)){cout << "Construct: &qu…

信号与线性系统翻转课堂笔记4——连续LTI系统的微分方程模型与求解

信号与线性系统翻转课堂笔记4——连续LTI系统的微分方程模型与求解 The Flipped Classroom4 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#x…

gitee提交代码步骤介绍(含git环境搭建)

1、gitee官网地址 https://gitee.com; 2、Windows中安装git环境 参考博客&#xff1a;《Windows中安装Git软件和TortoiseGit软件》&#xff1b; 3、设置用户名和密码 这里的用户名和密码就是登录gitee网站的用户名和密码如果设置错误&#xff0c;可以在Windows系统的“凭据管理…

Kubernetes (k8s) 快速认知

应用部署方式 传统部署时代 早期的时候&#xff0c;各个组织是在物理服务器上运行应用程序。缺点 资源分配问题&#xff1a; 无法限制在物理服务器中运行的应用程序资源使用 维护成本问题&#xff1a; 部署多个物理机&#xff0c;维护许多物理服务器的成本很高 虚拟化部署时…

论文修改润色算学术不端吗 快码论文

大家好&#xff0c;今天来聊聊论文修改润色算学术不端吗&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文修改润色是否算学术不端&#xff1f;专业软件…

U-boot启动流程与加载内核过程

目录 一、U-boot启动过程流程图二、U-boot启动过程函数简单注释 本篇文章梳理了一下对正点原子的驱动开发教程中u-boot启动流程的梳理&#xff0c;制作了一份流程图&#xff0c;并简单的记录了一下各函数的作用&#xff0c;方便回头翻阅。 一、U-boot启动过程流程图 二、U-boot…

git-lfs基本知识讲解

目录 1. 基本知识2. 安装 1. 基本知识 git-lfs 是 Git Large File Storage 的缩写&#xff0c;是 Git 的一个扩展&#xff0c;用于处理大文件的版本控制。 它允许你有效地管理和存储大型二进制文件&#xff0c;而不会使 Git 仓库变得过大和不稳定。以下是一些与 git-lfs 相关…

机器学习——自领域适应作业

任务 游戏里面的话有很多跟现实不一样的情况。 想办法让中间的特征更加的接近&#xff0c;让feat A适应feat B&#xff0c;产生相对正常的输出。 在有标签数据和没有数据的上面进行训练&#xff0c;并能预测绘画图像。 数据集 训练5000张总数&#xff0c;每类有500张测试100…

Jmeter实现服务器端后台接口性能测试!

实现目的 在进行服务器端后台接口性能测试时&#xff0c;需要连接到Linux服务器端&#xff0c;然后通过命令调用socket接口&#xff0c;这个过程就需要用到jmeter的SSH Command取样器实现了。 脚本实现 设置CSV Data Set ConFig配置元件&#xff0c;参数化测试数据 设置SSH…

【线性代数】期末速通!

1. 行列式的性质 1.1 求一个行列式的值 特殊地&#xff0c;对角线左下全为0&#xff0c;结果为对角线乘积。行 r 列 c 1.2 性质 某行&#xff08;列&#xff09;加上或减去另一行&#xff08;列&#xff09;的几倍&#xff0c;行列式不变某行&#xff08;列&#xff09;乘 …

118. 杨辉三角

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2: 输入: numRows 1 输出: [[1]]提示: 1 <…

Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现实时通信

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

08 v-text指令

概述 v-text指令主要是用来渲染文本内容&#xff0c;和双大括号的效果基本一致&#xff0c;所以使用场景非常少。 一般情况下&#xff0c;我们都会使用双大括号语法去渲染文本内容&#xff0c;而不是使用v-text指令。 基本用法 我们创建src/components/Demo08.vue&#xff…

web服务器之——基于虚拟目录和用户控制的web网站

目录 一、虚拟目录 虚拟目录的作用&#xff1a; 二、搭建基于虚拟目录的web网站 1、www服务器配置 2、搭建静态网站 设置防火墙状态 关闭文件访问权限——SeLinux 3、编辑网页资源文件 4、设置虚拟目录 5、向虚拟目录中写入资源 6、重启httpd 三、搭建基…

SCI一区级 | Matlab实现GWO-CNN-GRU-selfAttention多变量多步时间序列预测

SCI一区级 | Matlab实现GWO-CNN-GRU-selfAttention多变量多步时间序列预测 目录 SCI一区级 | Matlab实现GWO-CNN-GRU-selfAttention多变量多步时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-CNN-GRU-selfAttention灰狼算法优化卷积门控循环…

气候灾害组织:全球红外降水站数据

气候灾害组织红外降水站数据-Prelim (CHIRPS-Prelim) 气候灾害中心红外降水站数据 Prelim (CHIRPS-Prelim) 融合了 CHIRPS 数据与原位降水数据&#xff0c;以消除数据偏差并提高其准确性。生成 CHIRPS-Prelim 的过程与 CHIRPS 过程类似&#xff0c;主要区别在于它仅依赖于近实…

ripro后台登录后转圈和图标不显示的原因及解决方法

最近&#xff0c;好多小伙伴使用ripro主题的小伙伴们都发现&#xff0c;登录后台后&#xff0c;进入主题设置就转圈&#xff0c;等待老半天后好不容易显示页面了&#xff0c;却发现图标不显示了&#xff0c;都统一显示为方框。 这是因为后台的js、css这类静态资源托管用的是js…

快速排序(一)

目录 快速排序&#xff08;hoare版本&#xff09; 初级实现 问题改进 中级实现 时空复杂度 高级实现 三数取中 快速排序&#xff08;hoare版本&#xff09; 历史背景&#xff1a;快速排序是Hoare于1962年提出的一种基于二叉树思想的交换排序方法 基本思想&#xff1a…