FreeRTOSFreeRTOS列表和列表项

news2024/11/17 0:31:05

FreeRTOS列表和列表项

今天继续跟着正点原子学习FreeRTOS列表和列表项的内容。列表和列表项这个知识点用到了C语言链表的知识点。所以必须对C语言中的链表这个数据结构才能更好的理解这部分内容。TIPS:正点原子这节课内容讲的特别好,强烈推荐:第20讲列表和列表项简介

什么是列表和列表项

列表就是FreeRTOS中的数据结构,它被用来跟踪FreeRTOS中的任务。主要的列表包括:

  1. 就绪列表(Ready Lists):对于每个优先级,FreeRTOS都维护一个就绪列表。当任务处于就绪状态,能够运行,但由于CPU正被其他任务使用而无法立即执行时,该任务会被放入相应优先级的就绪列表中。当CPU可用时,调度器会从最高优先级的就绪列表中选择任务来执行。

  2. 阻塞列表(Blocked Lists):当任务等待某个事件(如信号量、互斥量或定时器)时,它会被放入阻塞列表。阻塞列表用于跟踪哪些任务正在等待资源变得可用。当等待的事件发生时,任务可以从阻塞列表移回就绪列表。

  3. 挂起列表(Suspended List):当任务被显式挂起(例如,调用了vTaskSuspend函数)时,它会被放入挂起列表。挂起的任务不会被调度器考虑运行,直到它们被显式地恢复(例如,调用了vTaskResume)。

  4. 延时列表(Delay Lists):当任务需要延迟执行或等待一段时间时,它会被放入延时列表。这通常用于实现非阻塞延时或定时功能。延时列表实际上分为两个:一个用于短期延时(当前延时列表),另一个用于长期延时(溢出延时列表)。这种分法帮助管理时间的回绕问题。

  5. 终止列表(Terminated List):某些FreeRTOS配置允许已删除或已结束的任务被放入终止列表,直到它们的任务控制块(TCB)被回收。这不是所有FreeRTOS配置都支持的功能。
    每个列表都是通过链表实现的,每个链表节点都包含了指向任务控制块(Task Control Block, TCB)的指针。TCB是FreeRTOS用来存储任务状态、堆栈指针和其他必要信息的结构。
    FreeRTOS的调度器会根据这些列表及其内任务的状态和优先级来决定哪个任务应该获得CPU时间。通过这种方式,FreeRTOS实现了任务的优先级调度和时间共享调度。与列表有关的内容都在list.c和list.h中。在list.h中定义一个叫做List_t的结构体。下面我们来看一下:

typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE      /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    volatile UBaseType_t uxNumberOfItems;
    ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
    MiniListItem_t xListEnd;                  /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
    listSECOND_LIST_INTEGRITY_CHECK_VALUE     /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

在FreeRTOS中,List_t结构体是用于实现各种任务列表(如就绪列表、阻塞列表等)的核心数据结构。下面是List_t结构体各个成员的作用解释:

  1. listFIRST_LIST_INTEGRITY_CHECK_VALUE:这是一个可选的完整性检查值,仅在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它的目的是帮助开发者检测内存损坏或不正确的列表操作。这个值通常被设置为一个特定的常量,以便在运行时检查列表的完整性。

  2. volatile UBaseType_t uxNumberOfItems:这个成员变量用来记录列表中项目的数量。它是volatile类型的,因为它可能会在中断服务例程和普通任务代码之间共享,确保编译器在每次访问时都会从内存中重新读取其值,而不是使用可能已经过时的寄存器副本。

  3. ListItem_t * configLIST_VOLATILE pxIndex:这是一个指向列表中某个项目的指针,用作遍历列表的索引。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,pxIndex以正确的方式被视为volatilepxIndex通常用于通过listGET_OWNER_OF_NEXT_ENTRY()宏来遍历列表,每次调用都会返回列表中的下一个元素。

  4. MiniListItem_t xListEnd:这个成员是列表的一个哨兵(或标记)元素,表示列表的末尾。xListEndxItemValue通常被设置为最大可能的值,以确保它总是位于列表的末尾。这个哨兵元素使得列表操作(如插入和删除)可以在不需要特殊情况处理的情况下进行,因为列表始终至少包含一个元素(即xListEnd)。

  5. listSECOND_LIST_INTEGRITY_CHECK_VALUE:这是第二个可选的完整性检查值,其作用与listFIRST_LIST_INTEGRITY_CHECK_VALUE相同,提供额外的完整性检查。这也只有在configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时才会启用。
    至于第四点为啥要有一个哨兵元素,如果有链表操作知识的基础,在没有哨兵元素的链表中,插入和删除操作需要检查列表是否为空,以及操作是否发生在列表的开头或末尾。这增加了实现的复杂性。使用哨兵元素后,这些特殊情况的处理被统一化,因为列表永远不会为空(至少有一个哨兵元素),且不需要单独处理列表的末尾。统一插入和删除操作:哨兵元素确保每个插入和删除操作都有一个统一的前驱节点和后继节点,无论操作发生在列表的哪个位置。这意味着代码可以使用相同的逻辑来处理所有情况。

在这里插入图片描述
所以列表中忽略那两个检查元素,所有的具体的成员如上图所示。
接下来让我们来看下列表项和迷你列表项。其中迷你列表项就是xlistend,也就是列表标记的哨兵元素。首先来看下列表项和迷你列表项所拥有成员的差异,具体差异如下图所示。
在这里插入图片描述
可以看出迷你列表项只有三个成员。xMINI_LIST_ITEM结构体是FreeRTOS中用来表示列表中的一个元素或节点的数据结构。它是ListItem_t的简化版本,通常用于实现哨兵元素(如xListEnd),以简化列表操作。下面是xMINI_LIST_ITEM结构体每个成员的详细解释:

. configLIST_VOLATILE TickType_t xItemValue:这个成员变量用于存储元素的排序值或优先级。TickType_t通常是一个无符号整型,用于表示时间或者计数。在FreeRTOS中,列表经常根据这个值进行排序,例如,用于管理延时任务的列表就是按照xItemValue的值(表示唤醒时间)进行排序的。configLIST_VOLATILE是一个宏,用于确保在特定的编译器或配置下,xItemValue以正确的方式被视为volatile。这是因为xItemValue可能会在中断服务例程中被修改。

struct xLIST_ITEM * configLIST_VOLATILE pxNext:这是一个指向列表中下一个元素的指针。它使得列表可以以链表的形式进行遍历。configLIST_VOLATILE确保pxNext在需要时被正确处理为volatile,以适应可能的并发修改。

struct xLIST_ITEM * configLIST_VOLATILE pxPrevious:这是一个指向列表中前一个元素的指针。它的存在使得列表成为一个双向链表,允许从任一方向遍历列表。这对于某些操作,如从列表中删除一个元素,是非常有用的,因为可以直接访问前一个元素,从而简化了指针的重新链接过程。同样,configLIST_VOLATILE确保在特定配置下,pxPrevious被正确地视为volatile
至于为啥非要整出来一个迷你列表项,而不直接全部都用列表项。这可能会让人疑惑它们之间的区别以及为什么需要两种不同的结构体。主要区别通常在于它们的用途和设计意图,而不仅仅是结构体成员本身。下面是创建迷你列表项的原因。

1. 简化和优化

xMINI_LIST_ITEMListItem_t的简化版本。虽然它们的成员变量可能相似,xMINI_LIST_ITEM通常用于特定场景,如作为哨兵节点(xListEnd)或在不需要完整ListItem_t功能的场合。这种简化有助于减少内存占用和提高代码效率,尤其是在资源受限的嵌入式系统中。

2. 特定用途

xMINI_LIST_ITEM通常用于实现列表的哨兵节点,这是一个始终存在于列表末尾的特殊节点,用以简化列表操作逻辑。而ListItem_t则用于表示实际的列表数据项。这种区分使得代码更加清晰,逻辑更加简单。

3. 减少复杂性

在某些情况下,列表项可能不需要ListItem_t提供的所有功能。例如,哨兵节点不需要存储额外的数据(如所属任务的指针或其他用户定义数据)。在这种情况下,使用更简单的xMINI_LIST_ITEM可以减少实现的复杂性,同时保持足够的功能性。

4. 内存效率

在资源受限的嵌入式系统中,每个字节的内存都很宝贵。xMINI_LIST_ITEM由于其简化的设计,占用的内存可能比ListItem_t少。在只需要基本链表功能(如哨兵节点)时,使用xMINI_LIST_ITEM可以节省宝贵的系统资源。
那了解了迷你列表项和成员变量和其作用后下面我们来了解下,列表项中各个成员的作用。
具体代码如下:

struct xLIST_ITEM
{
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    configLIST_VOLATILE TickType_t xItemValue;          /*< The value being listed.  In most cases this is used to sort the list in ascending order. */
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;     /*< Pointer to the next ListItem_t in the list. */
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
    void * pvOwner;                                     /*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */
    struct xLIST * configLIST_VOLATILE pxContainer;     /*< Pointer to the list in which this list item is placed (if any). */
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t;                   /* For some reason lint wants this as two separate definitions. */

ListItem_t(或struct xLIST_ITEM)是FreeRTOS中用于构建双向链表的核心数据结构之一。它比xMINI_LIST_ITEM更为复杂,提供了更多的功能和灵活性。

  1. listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE: 当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时,此成员被设置为一个已知的值,用于运行时检查列表项的完整性。这有助于开发者识别内存损坏或不当的列表操作,增加了代码的健壮性。

  2. configLIST_VOLATILE TickType_t xItemValue: 这是列表项的值,通常用于排序列表。在大多数情况下,列表是按照xItemValue的升序排列的。例如,FreeRTOS的延时任务列表就是根据任务唤醒时间(用xItemValue表示)进行排序的。TickType_t是一个基于配置的类型,通常为无符号整型,代表时间或计数。configLIST_VOLATILE确保此成员在需要时被视为volatile,适用于可能在中断服务例程中修改的场景。

  3. struct xLIST_ITEM * configLIST_VOLATILE pxNext: 指向链表中下一个ListItem_t的指针。这使得可以从当前列表项向后遍历整个列表。

  4. struct xLIST_ITEM * configLIST_VOLATILE pxPrevious: 指向链表中前一个ListItem_t的指针。这使得可以从当前列表项向前遍历整个列表,是双向链表结构的关键组成部分。

  5. void * pvOwner: 指向包含此列表项的对象的指针。通常,这个指针指向一个任务控制块(TCB),但也可以指向其他使用列表项的数据结构。这实现了对象和其所属列表项之间的双向链接。

  6. struct xLIST * configLIST_VOLATILE pxContainer: 指向包含此列表项的列表的指针。这个成员变量使得可以从列表项访问其所属的列表,进而实现诸如移除列表项或调整列表结构等操作。

  7. listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE: 类似于listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE,这是另一个完整性检查值,当configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1时使用。它位于结构体的末尾,为列表项提供额外的内存完整性验证。
    上面已经介绍完了列表、列表项、迷你列表项的结构以及成员变量,接下来来看下如何形成双向链表。这一点视频里面讲的很好。直接来看视频这个地方:
    在这里插入图片描述
    其实双向环形链表就是三个人拉成一个圈一样,小明的右手指向小黑的左手,小明的左手指向小红的右手。小红、小黑的过程类似。依次形成了双向环形链表。
    比如这里列表有两个列表项成员,如下图所示:详细PPT可以看正点原子开源网中课件与源码:

在这里插入图片描述
我们从这个图可以看出来,列表项1相当于为头,列表项2为第二项,末尾列表项就是在末尾。所以末尾列表项前一个就是列表项2,末尾列表项下一个就是头,因为是双向环形链表。也就是列表项1,列表项2前一个就是就是列表项1,所以指向列表项1,列表项2下一个是末尾列表项,而列表一前一个就是末尾列表项,下一个就是列表项2.具体指向关系就是上图所示。

列表和列表项API函数

列表和列表项API函数
我们去FreeRTOS的具体工程去看上述函数的具体实现代码:

void vListInitialise( List_t * const pxList )

void vListInitialise( List_t * const pxList )
{
    /* The list structure contains a list item which is used to mark the
     * end of the list.  To initialise the list the list end is inserted
     * as the only list entry. */
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*这行代码将链表的索引(pxIndex)设置为链表结束标志(xListEnd)的地址。在FreeRTOS中,每个链表都有一个特殊的列表项作为链表的结束标志,这个特殊的列表项不存储任何用户数据,它的主要目的是标记链表的末尾。 */

    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );

    /* 这行代码设置列表结束标志的xItemValue为portMAX_DELAY,这是一个非常大的值,确保结束标志始终位于链表的最末端。 */
    pxList->xListEnd.xItemValue = portMAX_DELAY;

    /* The list end next and previous pointers point to itself so we know
     * when the list is empty. */
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );     /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*这两行代码使列表结束标志的pxNext和pxPrevious指针都指向自身,这样做的目的是在链表为空时,能够通过检查这些指针来快速识别。 */

    /* 如果配置没有使用迷你列表项(configUSE_MINI_LIST_ITEM为0),则将xListEnd的pvOwner和pxContainer字段初始化为NULL,并设置完整性检查值。这是为了确保列表结束标志作为一个完整的列表项符合预期的数据完整性要求 */
    #if ( configUSE_MINI_LIST_ITEM == 0 )
    {
        pxList->xListEnd.pvOwner = NULL;
        pxList->xListEnd.pxContainer = NULL;
        listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
    }
    #endif

    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//这行代码将链表中的项数初始化为0,表示链表是空的。

    /* Write known values into the list if
     * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

void vListInitialiseItem( ListItem_t * const pxItem )

void vListInitialiseItem( ListItem_t * const pxItem )
{
    /* Make sure the list item is not recorded as being on a list. */
    pxItem->pxContainer = NULL;

    /* Write known values into the list item if
     * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )

void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )
{
    ListItem_t * const pxIndex = pxList->pxIndex;/*这行代码定义了一个指
    向ListItem_t的常量指针pxIndex,并将其初始化为指向pxList中的pxIndex成员。pxIndex通常用于标记链表的
    起始位置或用于遍历链表。*/

    /* Only effective when configASSERT() is also defined, these tests may catch
     * the list data structures being overwritten in memory.  They will not catch
     * data errors caused by incorrect configuration or use of FreeRTOS. */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* Insert a new list item into pxList, but rather than sort the list,
     * makes the new list item the last item to be removed by a call to
     * listGET_OWNER_OF_NEXT_ENTRY(). */
    pxNewListItem->pxNext = pxIndex;
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;/*由于要将这个列表项插入到pxindex的前一个。
    由于是双向环形链表,所以相当于该列表项伸出来2只手,一个拉前面,一个拉后面*/

    /* Only used during decision coverage testing. */
    mtCOVERAGE_TEST_DELAY();

    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;/*新列表项已经伸出来2只手,那么前面的是不是也要伸出来一只手连接到新的列表项。
    那新的列表项后面那个是不是也要往前面伸出来一只手与之连接才能形成双向环形链表*/

    /* Remember which list the item is in. */
    pxNewListItem->pxContainer = pxList;

    ( pxList->uxNumberOfItems )++;
}

上面这段函数如果对链表操作熟悉的话,就其实是把pxNewListItem 插入pxIndex前面的链表操作。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )

/*-----------------------------------------------------------*/

void vListInsert( List_t * const pxList,
                  ListItem_t * const pxNewListItem )
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

    /* Only effective when configASSERT() is also defined, these tests may catch
     * the list data structures being overwritten in memory.  They will not catch
     * data errors caused by incorrect configuration or use of FreeRTOS. */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* 如果xValueOfInsertion的值等于portMAX_DELAY,这通常表示一个特殊的值,
    用于将新的列表项插入到列表的末尾。因此,pxIterator被设置为指向列表末尾前的项(pxList->xListEnd.pxPrevious)。
    如果xValueOfInsertion不等于portMAX_DELAY,则通过一个循环找到新列表项应
    该插入的位置。循环遍历列表,直到找到一个其xItemValue大于xValueOfInsertion的项。pxIterator在这个过程中用于遍历列表。 */
    if( xValueOfInsertion == portMAX_DELAY )
    {
        pxIterator = pxList->xListEnd.pxPrevious;
    }
    else
    {
		/*该循环用来寻找插入的正确位置,这个函数插入是按照数值升序去插入的。首先循环的初始值就是哨兵节点的地址,
		由于要找到合适的插入位置,也就是终止循环的条件是pxIterator->pxNext->xItemValue > xValueOfInsertion。
		所以此时的pxIterator就是要插入位置的前一个节点。
		*/
        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM.  This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
        {
            /* There is nothing to do here, just iterating to the wanted
             * insertion position. */
        }
    }
	/*这里更好的理解就是理解为手拉手操作,要更新插入新的人前后指向位置,以及新的人前面的人指向后的位置。还有新的人后面的人指向前面的位置,这样才能形成双向链表。这里用图其实更好理解*/
    pxNewListItem->pxNext = pxIterator->pxNext;
    pxNewListItem->pxNext->pxPrevious = pxNewListItem;
    pxNewListItem->pxPrevious = pxIterator;
    pxIterator->pxNext = pxNewListItem;

    /* Remember which list the item is in.  This allows fast removal of the
     * item later. */
    pxNewListItem->pxContainer = pxList;

    ( pxList->uxNumberOfItems )++;
}
/*-----------------------------------------------------------*/

这个函数完成链表操作我用图大概画了下:
在这里插入图片描述
这样理解为牵手更容易去理解。
下面来看最后一个函数也就是删除链表中某个节点。删除要进行的操作就是

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list
 * item. */
    List_t * const pxList = pxItemToRemove->pxContainer;

    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

    /* Only used during decision coverage testing. */
    mtCOVERAGE_TEST_DELAY();

    /* Make sure the index is left pointing to a valid item. */
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    pxItemToRemove->pxContainer = NULL;
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;
}

在这里插入图片描述

实际要完成的操作就是把1,2,3,4都给断掉,变成蓝色的。
所以要完成代码的操作就是下面这个:
1:pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
2:pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
在这里插入图片描述

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

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

相关文章

《QT实用小工具·十八》高亮发光按钮控件

1、概述 源码放在文章末尾 该项目实现了高亮发光按钮控件 可设置文本&#xff0c;居中显示。可设置文本颜色。可设置外边框渐变颜色。可设置里边框渐变颜色。可设置背景色。可直接调用内置的设置 绿色、红色、黄色、黑色、蓝色 等公有槽函数。可设置是否在容器中可移动&#…

2024 抖音欢笑中国年(二):AnnieX互动容器创新玩法解析

本文基于24年抖音春节活动业务背景&#xff0c;介绍了字节跨端容器AnnieX在游戏互动套件上的探索&#xff0c;致力于提升容器在游戏互动场景的优化能力。 业务背景 AnnieX作为字节一方游戏统一容器&#xff0c;服务字节内部电商、直播、UG等跨端场景业务。在字节一方游戏互动场…

YOLOv8模型剪枝实战:Network Slimming网络瘦身方法

课程链接&#xff1a;YOLOv8模型剪枝实战&#xff1a;Network Slimming网络瘦身方法_在线视频教程-CSDN程序员研修院 YOLOv8是一个当前非常流行的目标检测器&#xff0c;本课程使用Network Slimming&#xff08;网络瘦身&#xff09;剪枝方法对YOLOv8进行模型剪枝&#xff0c;…

springboot国际化多语言

1,新建国际化多语言文件 在resources目录下新建 messages.properties 其他语言的文件 编辑messages.properties文件,下方从text切换到Resource Bundle ,即可对照着编辑多语言文件 (如果没有找到Resource Bundle,先在settings->plugins中安装Resource Bundle Editor) 2,配…

学习 MongoDB:打开强大的数据库技术大门

一、基本概念 MongoDB 是一个基于分布式文件存储的文档数据库&#xff0c;由 C 语言编写。它旨在为 Web 应用提供可扩展的高性能数据存储解决方案。 相信MySQL我们非常的熟悉&#xff0c;那么MySQL的表结构与MongoDB的文档结构进行类比的话可能更好理解MongoDB。 MySQL的数据…

Spyder无法载入(load)或者闪退问题

在Anaconda prompt中直接输入spyder&#xff0c;报错如下 Traceback (most recent call last):File "C:\Users\user\.conda\envs\KB\Scripts\spyder-script.py", line 10, in sys.exit(main())File "C:\Users\user\.conda\envs\KB\lib\site-packages\spyder\a…

baseline SE SP YI是什么?

SE、SP和YI是评估分类模型性能时常用的几个统计指标&#xff0c;特别是在医学影像处理、疾病诊断等领域&#xff0c;这些指标帮助了解模型对于正负类样本的识别能力。 SE (Sensitivity)&#xff0c;也称为真正率&#xff08;True Positive Rate, TPR&#xff09;或召回率&#…

泛型(java学习)

目录 1.泛型介绍&#xff1a; 2.泛型的好处&#xff1a; 3.泛型的语法 4.泛型的细节 5.自定义泛型 6.自定义泛型接口 8.泛型的继承和通配符 1.泛型介绍&#xff1a; 1&#xff09;泛型又称参数化类型&#xff0c;解决数据类型的安全性问题。 2&#xff09;在类声明或实例…

5.3 用栈翻转数组,动态规划求斐波那契数列

5.3 用栈翻转数组&#xff0c;动态规划求斐波那契数列 1. 用栈翻转数组 assume cs:code,ds:data,ss:stack data segmentarr dw 1111h,2222h,3333h,4444h,5555h,6666h,7777h,8888hres db 800 dup(0) data endsstack segmentdb 100 dup(0) stack endscode segmentstart:mov ax,…

计算机视觉入门:开启图像理解之旅

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

【算法篇】三道题理解算法思想——认识BFS

BFS&#xff08;宽搜&#xff09; 宽度优先遍历和深度优先遍历组成了大家熟悉的搜索算法&#xff0c;这两种算法也是蓝桥杯之类竞赛题的常考思想&#xff0c;正巧马上蓝桥杯临近&#xff0c;博主也是刷了很多BFS相关的题型&#xff0c;在这篇文章中会从力扣上选取三道简单的宽搜…

小象超市(原美团买菜) 的大屏图表

文章目录 概要技术细节技术名词解释小结 概要 20203年12月1日&#xff0c;美团旗下自营零售品牌“美团买菜”升级为全新品牌“小象超市”。 &#xff0c;“小象超市”坚持美团自营零售模式&#xff0c;通过在社区设立的集存储、分拣、配送为一体的便民服务站&#xff0c;为社区…

关于16:9和4:3的有关知识,看这篇文章就差不多了

序言 在你拍照或录制视频之前,你需要考虑使用哪个纵横比。最常见的两种纵横比是16:9和4:3,但哪一种最适合你? 16:9的纵横比最适合视频,因为宽度比高度宽78%,这使你更容易在水平方向上适应更多画面,同时优化视频以适应现代屏幕。 同时,4:3的纵横比更适合摄影,因为宽度…

书籍《笔记的方法》读后感

读完《笔记的方法》有几周的时间&#xff0c;书里有些记录的内容&#xff0c;觉得非常有价值的&#xff0c;自己的观点&#xff0c;当下读书&#xff0c;其实并没有那么高大尚&#xff0c;就是存粹陶冶下情操&#xff0c;读书还是有一定作用的&#xff0c;毕竟看书只能慢慢来&a…

【PDF-XSS攻击】Java项目-上传文件-解决PDF文件XSS攻击

文章目录 背景解决pdfbox依赖控制器代码PdfUtils工具类 验证最后源码参考 背景 上传xss-pdf造成存储型xss因为在浏览器直接预览的PDF&#xff0c;而不是预览&#xff0c;所以安全部门认为会有XSS漏洞 解决 安全部门修复建议 1、根据白名单的标签和属性对数据进行过滤&#…

C++后端程序员如何在缺乏相关外包项目中寻找赚取外快的途径

作为一位专业的C后端程序员&#xff0c;你拥有扎实的技术功底和丰富的编程经验&#xff0c;然而在寻求外包项目的道路上&#xff0c;可能时常会发现市场上的项目并不都与C直接相关。但这并不意味着你就无法利用自己的技能来获取额外收入。本文将为你揭示一些即使在当前环境下&a…

力扣热题100_链表_138_随机链表的复制

文章目录 题目链接解题思路解题代码 题目链接 138. 随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&a…

【算法】动态规划练习(一)

目录 1137. 第 N 个泰波那契数 分析 代码 面试题 08.01. 三步问题 分析 代码 746. 使用最小花费爬楼梯 分析 代码 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波…

软件测试人员如何避免成为“背锅侠”

作为一名软件测试工程师&#xff0c;我们的角色可以算是“战场上的后勤”&#xff0c;战役的胜败和所有团队人员都息息相关。 但是难免碰到战役失败后&#xff0c;很多团队互相推脱的局面&#xff0c;而测试人员就是所有团队中的弱势群体&#xff0c;自然是首当其冲的背锅侠&a…

34.Python从入门到精通—Python3 正则表达式检索和替换

34.从入门到精通&#xff1a;Python3 正则表达式检索和替换 repl 参数是一个函数 正则表达式对象 正则表达式修饰符 - 可选标志 正则表达式模式* 正则表达式实例 检索和替换repl 参数是一个函数正则表达式对象正则表达式修饰符 - 可选标志正则表达式模式*正则表达式实例 检索和…