当前位置: 首页 > news >正文

嵌入式c语言的内存管理

目录

一、内存布局概述

二、栈(Stack)

2.1. 定义与用途

2.2. 内存分配与释放

2.3. 增长方向与大小限制

三、堆(Heap)

3.1. 定义与用途

3.2. 内存分配与释放

3.3. 增长方向与潜在问题

四、全局/静态存储区

4.1. 定义与用途

4.2. 内存分配与生命周期

4.3. 使用注意事项

五、内存池分配

5.1. 定义与原理

5.2. 优点

5.3. 适用场景

5.4. 实现方式

5.5. 注意事项

5.6. 示例代码

六、总结


在嵌入式系统开发中,内存管理是一个至关重要的环节。由于嵌入式系统通常资源有限,高效的内存管理不仅能够提升系统的性能,还能有效避免内存泄漏、栈溢出等问题。

一、内存布局概述

在嵌入式系统中,内存通常被划分为几个不同的区域,以满足程序运行的不同需求。这些区域包括栈(Stack)、堆(Heap)、全局/静态存储区等。

二、栈(Stack)

2.1. 定义与用途

栈是一种后进先出(LIFO,Last In First Out)的数据结构,意味着最后插入的元素会是第一个被移除的元素。在嵌入式C语言中,栈扮演着至关重要的角色,主要用于存储以下几类数据:

  • 局部变量:在函数内部声明的变量。
  • 函数参数:传递给函数的参数值。
  • 函数调用的返回地址:当函数被调用时,需要保存调用者的下一条指令地址,以便函数执行完毕后能够正确返回。

2.2. 内存分配与释放

  • 栈的内存管理完全由编译器自动完成,无需程序员手动干预。
  • 当函数被调用时,编译器会在栈上为该函数的局部变量和参数分配连续的内存空间
  • 函数执行过程中,这些变量和参数会按照后进先出的顺序被访问和修改。
  • 当函数执行完毕并准备返回时,编译器会自动回收之前为该函数分配的所有栈内存空间

以下是一个简单的代码示例,展示栈上局部变量的使用:

#include <stdio.h>void exampleFunction(int a, int b) {int localVar1 = a + b; // 局部变量存储在栈上int localVar2 = localVar1 * 2; // 另一个局部变量printf("localVar1 = %d, localVar2 = %d\n", localVar1, localVar2);
}int main() {exampleFunction(3, 4); // 调用函数,传递参数return 0;
}

exampleFunction函数的局部变量localVar1localVar2都被存储在栈上。当exampleFunction被调用时,编译器会在栈上为这两个变量分配空间。当函数执行完毕后,这些空间会被自动释放。

2.3. 增长方向与大小限制

栈的增长方向是从高地址向低地址进行的。意味着随着新元素的加入,栈顶指针会向低地址方向移动。在嵌入式系统中,由于内存资源有限,栈的大小通常是固定的。因此,如果函数中的局部变量过多或函数调用嵌套过深,就可能导致栈溢出(Stack Overflow)的错误。栈溢出会导致程序崩溃或行为异常,因此开发者需要谨慎设计函数和局部变量的大小,以避免这种情况的发生。

以下是一个可能导致栈溢出的代码示例:

#include <stdio.h>void recursiveFunction(int count) {if (count > 0) {char largeArray[1024]; // 分配一个较大的数组作为局部变量recursiveFunction(count - 1); // 递归调用自身}
}int main() {recursiveFunction(1000); // 调用递归函数,传入一个较大的参数值return 0;
}

recursiveFunction函数每次调用时都会分配一个1024字节的数组作为局部变量。如果递归调用的深度过大(例如这里的1000次),就可能导致栈内存耗尽,从而引发栈溢出错误。为了避免这种情况,开发者需要限制递归的深度或改用其他数据结构(如循环或队列)来替代递归。同时,在嵌入式系统中,合理设置栈的大小也是非常重要的。

三、堆(Heap)

3.1. 定义与用途

堆是用于动态内存分配的区域。与栈不同,栈的内存分配是自动的且生命周期与函数调用相关联,而堆的内存分配则由程序员手动控制,其生命周期由程序员显式管理。在嵌入式C语言中,堆提供了一种灵活的方式来分配和释放内存,这对于处理大小在编译时未知的数据结构或需要在程序运行时动态调整大小的数据结构特别有用。

3.2. 内存分配与释放

在C语言中,堆内存的分配和释放主要通过以下几个函数实现:

  • malloc():用于分配指定大小的内存块。如果分配成功,返回一个指向分配内存的指针;如果失败,则返回NULL。动态内存分配函数详解[1]:malloc()-CSDN博客
  • calloc():类似于malloc(),但它还会将分配的内存初始化为零。动态内存分配函数详解[2]:calloc()-CSDN博客
  • realloc():用于调整之前通过malloc()、calloc()或realloc()分配的内存块的大小。如果调整成功,它返回指向新内存块的指针(可能与原指针相同,也可能不同);如果失败,则返回NULL,并且原内存块保持不变。动态内存分配函数详解[3]:realloc()-CSDN博客
  • free():用于释放之前通过malloc()、calloc()或realloc()分配的内存块。释放后的内存块将不再可用,且应确保不再访问这些内存。动态内存分配函数详解[4]:free()_free函数c-CSDN博客

以下是一个简单的代码示例,展示如何在堆上分配和释放内存:

#include <stdio.h>
#include <stdlib.h>int main() {// 在堆上分配一个整数数组int numElements = 10;int *array = (int *)malloc(numElements * sizeof(int));if (array == NULL) {// 内存分配失败fprintf(stderr, "Memory allocation failed\n");return 1;}// 初始化数组for (int i = 0; i < numElements; i++) {array[i] = i * 2;}// 打印数组内容for (int i = 0; i < numElements; i++) {printf("%d ", array[i]);}printf("\n");// 释放分配的内存free(array);return 0;
}

3.3. 增长方向与潜在问题

堆的增长方向通常是从低地址向高地址进行的,但取决于具体的内存管理器和操作系统。在嵌入式系统中,由于内存资源有限,不正确地管理堆内存可能会导致严重的问题:

  • 内存泄漏:当程序员忘记释放已分配的内存时,这些内存将永远无法被重新使用,从而导致内存泄漏。随着时间的推移,内存泄漏可能会耗尽所有可用的堆内存,导致程序崩溃或无法继续运行。
  • 悬空指针:当程序员释放了内存但仍然保留指向该内存的指针时,就会创建一个悬空指针。尝试通过悬空指针访问内存可能会导致未定义的行为,包括程序崩溃或数据损坏。

为了避免这些问题,程序员应该:

  • 确保每次调用malloc()calloc()realloc()后都检查返回的指针是否为NULL。
  • 对于每个通过malloc()calloc()realloc()分配的内存块,确保在不再需要时调用free()来释放它。
  • 避免使用悬空指针。在释放内存后,将指针设置为NULL是一个好习惯,有助于防止意外地使用悬空指针。
  • 使用工具(如Valgrind)来检测内存泄漏和悬空指针等内存管理错误。

 【C语言】库函数常见的陷阱与缺陷(三):内存分配函数[1]--malloc-CSDN博客

【C语言】库函数常见的陷阱与缺陷(三):内存分配函数[2]--calloc-CSDN博客

【C语言】库函数常见的陷阱与缺陷(三):内存分配函数[3]--realloc-CSDN博客

【C语言】库函数常见的陷阱与缺陷(三):内存分配函数[4]--free-CSDN博客

四、全局/静态存储区

4.1. 定义与用途

全局/静态存储区是程序中用于存储全局变量和静态变量的内存区域。全局变量在整个程序运行期间都是可访问的,它们的作用域跨越所有函数。而静态变量的作用域则取决于其定义的位置:如果静态变量在函数外部定义,其作用域限于定义它的文件(即具有文件作用域);如果静态变量在函数内部定义,其作用域仅限于该函数(但生命周期仍然是整个程序运行期间)。

4.2. 内存分配与生命周期

全局/静态存储区的内存分配在程序编译时就已经确定。意味着在程序启动之前,这些变量的内存空间就已经被分配好了。静态变量(无论是在函数内部还是外部定义的)在程序的整个生命周期内都存在,并且只会在程序启动时初始化一次。对于全局变量,如果它们没有被显式初始化,则会被自动初始化为0(对于数值类型)或NULL(对于指针类型)

4.3. 使用注意事项

  • 内存占用:在嵌入式系统中,由于内存资源有限,过多地使用全局变量可能会占用大量内存空间,从而影响程序的性能和稳定性。
  • 作用域控制:静态变量提供了一种限制变量作用域的方法。在函数外部定义静态变量时,可以确保该变量只在一个文件内可见和使用。在函数内部定义静态变量时,可以创建一个只在该函数内部可见且具有持久生命周期的变量。
  • 初始化:全局变量和静态变量只会在程序启动时初始化一次。如果需要在程序运行时动态地改变这些变量的值,那么应该使用其他类型的变量(如堆内存分配的变量)。

以下代码示例,展示全局变量和静态变量的使用:

// file1.c
#include <stdio.h>// 全局变量,在整个程序中都可访问
int globalVar = 10;// 静态变量,具有文件作用域,只在file1.c中可见
static int fileScopeVar = 20;void printGlobalAndFileScopeVar() {printf("globalVar = %d, fileScopeVar = %d\n", globalVar, fileScopeVar);
}// file2.c
#include <stdio.h>// 尝试访问globalVar(可见,因为它是全局的)
extern int globalVar; // 声明全局变量// 尝试访问fileScopeVar(不可见,因为它是静态的且只限于file1.c)
// extern int fileScopeVar; // 这会导致编译错误void printGlobalVar() {printf("globalVar from file2.c = %d\n", globalVar);// printf("fileScopeVar from file2.c = %d\n", fileScopeVar); // 这会导致编译错误
}// main.c
#include <stdio.h>// 声明全局变量和静态函数(在file1.c中定义的)
extern void printGlobalAndFileScopeVar();
extern void printGlobalVar();// 静态函数,只在main.c中可见
static void printStaticFuncScopeVar() {static int staticFuncScopeVar = 30; // 静态局部变量,只在printStaticFuncScopeVar函数内部可见,但生命周期是整个程序运行期间printf("staticFuncScopeVar = %d\n", staticFuncScopeVar);staticFuncScopeVar++; // 每次调用时都会增加
}int main() {// 访问和打印全局变量printf("Initial globalVar in main = %d\n", globalVar);globalVar++; // 修改全局变量的值// 调用其他函数来打印变量printGlobalAndFileScopeVar();printGlobalVar();// 调用静态函数并观察静态局部变量的行为printStaticFuncScopeVar(); // 输出30printStaticFuncScopeVar(); // 输出31,因为staticFuncScopeVar的值在上一次调用后已经增加return 0;
}

globalVar是一个全局变量,它在file1.cfile2.cmain.c中都是可见的。fileScopeVar是一个具有文件作用域的静态变量,它只在file1.c中可见。printStaticFuncScopeVar函数中的staticFuncScopeVar是一个静态局部变量,它只在printStaticFuncScopeVar函数内部可见,但其生命周期是整个程序运行期间。每次调用printStaticFuncScopeVar时,staticFuncScopeVar的值都会增加。

五、内存池分配

内存池分配是一种高效的内存管理技术,特别适用于嵌入式系统和其他资源受限的环境。

5.1. 定义与原理

内存池分配是一种预分配策略,它在程序启动或某个特定时刻先申请一块或多块较大的内存区域作为“内存池”。当程序运行时,如果有小的内存块需求,就从已经分配好的内存池中划出一部分来使用,而不是每次都去调用系统的内存分配函数(如malloc)。

5.2. 优点

  • 提高效率:内存池分配减少了频繁调用系统内存分配函数所带来的开销,因为内存池是在程序启动时或某个阶段一次性分配的。

  • 减少碎片:由于内存池中的内存块是预先划分好的,因此可以避免因为频繁分配和释放小块内存而产生的内存碎片问题

  • 控制内存使用:使用内存池可以更精确地控制内存的使用情况,因为程序员知道内存池的大小和分配策略。

  • 提高响应速度:在需要快速响应的场景中,内存池分配可以显著减少内存分配的时间,因为内存已经预先准备好了。

5.3. 适用场景

内存池分配特别适用于以下场景:

  • 需要频繁分配和释放小块内存的程序:如操作系统内核、网络通信程序、实时控制系统等。
  • 内存资源受限的嵌入式系统:在这些系统中,内存碎片和分配延迟都可能是致命的问题。
  • 性能要求高的应用:如游戏、图形处理、金融交易系统等,这些应用对内存分配和释放的效率有很高的要求。

5.4. 实现方式

实现内存池分配通常需要考虑以下几个方面:

  • 内存池的初始化:在程序启动时或某个特定时刻,分配一块或多块较大的内存区域作为内存池。

  • 内存块的划分:根据需求,将内存池划分为多个固定大小或可变大小的内存块。这些内存块可以是连续的,也可以是链表的形式。

  • 内存块的分配与释放:当需要内存时,从内存池中取出一个或多个内存块;当不再需要时,将这些内存块归还给内存池(尽管在某些实现中,内存块可能不会被立即释放回内存池,而是等到内存池被重新初始化或程序结束时才释放)。

  • 内存池的管理:维护一个或多个内存池的数据结构,记录内存池的状态(如已分配、空闲等)和内存块的分布情况。

  • 扩展与收缩:在某些实现中,内存池的大小可以是动态的,即根据需要扩展或收缩内存池的大小。通常涉及到更复杂的内存管理策略和数据结构。

5.5. 注意事项

  • 内存池的大小:需要根据应用的需求和系统的资源来确定内存池的大小。过大的内存池可能会浪费内存资源,而过小的内存池则可能导致频繁的内存池扩展和碎片问题。
  • 内存块的划分:需要根据应用的需求来确定内存块的大小和数量。如果内存块的大小不合适,可能会导致内存浪费或内存不足的问题。
  • 内存池的管理:需要设计合理的数据结构和算法来管理内存池的状态和内存块的分布情况。涉及到并发控制、错误处理等方面的问题。
  • 内存泄漏:即使使用了内存池分配,也需要注意内存泄漏的问题。如果内存块被分配后没有被正确释放(尽管在内存池中释放可能意味着归还给内存池而不是真正释放给系统),仍然会导致内存泄漏。因此,需要设计合理的内存管理机制来跟踪和释放内存块。

5.6. 示例代码

以下是一个简单的C语言内存池分配示例。这个示例展示如何创建一个固定大小的内存池,并从池中分配和释放内存块。请注意,此示例是为了教学目的而编写的,并未涵盖所有可能的错误处理和优化。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define POOL_SIZE 1024         // 内存池总大小(字节)
#define BLOCK_SIZE 32          // 每个内存块的大小(字节)
#define NUM_BLOCKS (POOL_SIZE / BLOCK_SIZE) // 内存池中可分配的内存块数量typedef struct MemoryBlock {struct MemoryBlock* next; // 指向下一个空闲内存块的指针(用于链表管理)bool is_free;             // 标记内存块是否空闲// 可以在这里添加其他数据,但会占用内存块的有效载荷空间
} MemoryBlock;typedef struct MemoryPool {MemoryBlock* free_list;   // 指向空闲内存块链表的头指针char pool[POOL_SIZE];     // 内存池的实际存储区域
} MemoryPool;// 初始化内存池
void initialize_memory_pool(MemoryPool* pool) {pool->free_list = (MemoryBlock*)pool->pool; // 将内存池的开始作为空闲链表的头for (int i = 0; i < NUM_BLOCKS - 1; i++) {// 设置每个内存块的下一个指针,并标记为空闲((MemoryBlock*)(pool->pool + i * BLOCK_SIZE))->next = (MemoryBlock*)(pool->pool + (i + 1) * BLOCK_SIZE);((MemoryBlock*)(pool->pool + i * BLOCK_SIZE))->is_free = true;}// 最后一个内存块的下一个指针设置为NULL,并标记为空闲((MemoryBlock*)(pool->pool + (NUM_BLOCKS - 1) * BLOCK_SIZE))->next = NULL;((MemoryBlock*)(pool->pool + (NUM_BLOCKS - 1) * BLOCK_SIZE))->is_free = true;
}// 从内存池中分配内存块
void* allocate_from_pool(MemoryPool* pool) {if (pool->free_list == NULL) {// 没有空闲内存块return NULL;}// 从空闲链表中取出第一个内存块MemoryBlock* block = pool->free_list;pool->free_list = block->next; // 更新空闲链表的头指针block->is_free = false;        // 标记内存块为已分配return (void*)(block + 1);     // 返回内存块的有效载荷部分(跳过MemoryBlock结构体本身)
}// 释放内存块回内存池(注意:这里只是归还给内存池,并不真正释放给系统)
void release_to_pool(MemoryPool* pool, void* ptr) {if (ptr == NULL) {// 无效的指针return;}// 将指针转换回MemoryBlock指针,并向前移动一个MemoryBlock结构体的大小MemoryBlock* block = (MemoryBlock*)((char*)ptr - sizeof(MemoryBlock));block->is_free = true; // 标记内存块为空闲// 这里为了简化,不直接将内存块插入到空闲链表的正确位置,而是放在链表头部(可能会导致性能问题)block->next = pool->free_list;pool->free_list = block;
}int main() {MemoryPool pool;initialize_memory_pool(&pool);// 分配和释放一些内存块void* block1 = allocate_from_pool(&pool);void* block2 = allocate_from_pool(&pool);printf("Allocated block1 and block2.\n");release_to_pool(&pool, block1);printf("Released block1 back to the pool.\n");// 尝试再次分配,应该能够重用之前释放的内存块void* block3 = allocate_from_pool(&pool);if (block3 == block1) {printf("block3 reused the memory of block1 (expected).\n");} else {printf("block3 did not reuse the memory of block1 (unexpected).\n");}// 注意:在实际应用中,应该继续分配和释放内存块,并检查内存泄漏和碎片问题。// 此外,这个示例没有处理并发访问和错误情况,这些在实际应用中都是非常重要的。return 0;
}

六、总结

综上所述,嵌入式C语言的内存管理是一个复杂而重要的任务。通过合理规划内存使用、选择合适的内存分配方式、掌握内存管理技巧以及编写高质量的代码,可以确保嵌入式系统的稳定性和性能。

相关文章:

嵌入式c语言的内存管理

目录 一、内存布局概述 二、栈&#xff08;Stack&#xff09; 2.1. 定义与用途 2.2. 内存分配与释放 2.3. 增长方向与大小限制 三、堆&#xff08;Heap&#xff09; 3.1. 定义与用途 3.2. 内存分配与释放 3.3. 增长方向与潜在问题 四、全局/静态存储区 4.1. 定义与用…...

uniapp-vue3 实现, 一款带有丝滑动画效果的单选框组件,支持微信小程序、H5等多端

采用 uniapp-vue3 实现, 是一款带有丝滑动画效果的单选框组件&#xff0c;提供点状、条状的动画过渡效果&#xff0c;支持多项自定义配置&#xff0c;适配 web、H5、微信小程序&#xff08;其他平台小程序未测试过&#xff0c;可自行尝试&#xff09; 可到插件市场下载尝试&…...

【Linux】shell脚本编程

目录 概念&#xff1a; shell脚本的本质&#xff1a; shell脚本编程&#xff1a; shell变量&#xff1a; 变量的定义格式&#xff1a; 变量的分类 自定义变量&#xff1a; 环境变量&#xff1a; 命令变量与命令行参数&#xff1a; 预定义变量&#xff1a; shell中的…...

ingress-nginx-controller安装

ingress-nginx-controller安装 ingress-nginx-controller是配置ingress发布的基础。以下主要采用Helm安装。地址&#xff1a; GitHub - kubernetes/ingress-nginx: Ingress NGINX Controller for Kubernetes 1 Helm安装 安装不难&#xff0c;需要找到合适的压缩包就行。我自…...

机器学习基础-机器学习的常用学习方法

目录 半监督学习的概念 规则学习的概念 基本概念 机器学习里的规则 逻辑规则 规则集 充分性与必要性 冲突消解 命题逻辑 → 命题规则 序贯覆盖 单条规则学习 剪枝优化 强化学习的概念 1. 强化学习对应了四元组 2. 强化学习的目标 强化学习常用马尔可夫决策过程…...

如何在Windows 11 WSL2 Ubuntu 环境下安装和配置perf性能分析工具?

在Windows 11 WSL2 Ubuntu 环境下完整安装和配置perf性能分析工具 一、背景二、准备工作三、获取并编译Linux内核源码四、安装和配置perf五、测试perf六、总结 一、背景 由于WSL2使用的是微软定制的内核&#xff0c;并非标准的Ubuntu内核&#xff0c;因此直接使用apt安装linux…...

人工智能知识分享第十天-机器学习_聚类算法

聚类算法 1 聚类算法简介 1.1 聚类算法介绍 一种典型的无监督学习算法&#xff0c;主要用于将相似的样本自动归到一个类别中。 目的是将数据集中的对象分成多个簇&#xff08;Cluster&#xff09;&#xff0c;使得同一簇内的对象相似度较高&#xff0c;而不同簇之间的对象相…...

使用wav2vec 2.0进行音位分类任务的研究总结

使用wav2vec 2.0进行音位分类任务的研究总结 原文名称&#xff1a; Using wav2vec 2.0 for phonetic classification tasks: methodological aspects 研究背景 自监督学习在语音中的应用 自监督学习在自动语音识别任务中表现出色&#xff0c;例如说话人识别和验证。变换器模型…...

【Leetcode 热题 100】33. 搜索旋转排序数组

问题背景 整数数组 n u m s nums nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c; n u m s nums nums 在预先未知的某个下标 k ( 0 ≤ k < n u m s . l e n g t h ) k(0 \le k \lt nums.length) k(0≤k<nums.length) 上进行了 旋转&…...

【VScode】设置代理,通过代理连接服务器

文章目录 VScode编辑器设置代理1.图形化界面1.1 进入proxy设置界面1.2 配置代理服务器 2.配置文件&#xff08;推荐&#xff09;2.1 打开setting.json 文件2.2 配置代理 VScode编辑器设置代理 根据情况安装nmap 1.图形化界面 1.1 进入proxy设置界面 或者使用快捷键ctrl , 。…...

每日一题-两个链表的第一个公共结点

文章目录 两个链表的第一个公共结点问题描述示例说明示例 1示例 2 方法及实现方法描述代码实现 复杂度分析示例运行过程示例 1示例 2 总结备注 两个链表的第一个公共结点 问题描述 给定两个无环的单向链表&#xff0c;找到它们的第一个公共节点。如果没有公共节点&#xff0c…...

Linux存储管理之核心秘密(The Core Secret of Linux Storage Management)

Linux存储管理之核心秘密 如果你来自Windows环境&#xff0c;那么Linux处理和管理存储设备的方式对你而言可能显得格外不同。我们知道&#xff0c;Linux的文件系统并不采用Windows那样的物理驱动器表示方式&#xff08;如C:、D:或E:&#xff09;&#xff0c;而是构建了一个以&…...

js单例模式

保证一个类只有一个实例,并提供一个访问它的全局访问点 实现 静态方法实现 class SingleTon{//全局的访问点static getInstance(){// 保证一个类只有一个实例if(!this.instance){this.instancenew SingleTon()}return this.instance}}let aSingleTon.getInstance()let bSing…...

搭建一个本地轻量级且好用的学习TypeScript语言的环境

需求说明 虽然 TypeScript 的在线 Playground 很方便 https://www.tslang.com.cn/play/&#xff0c;但毕竟是在浏览器中使用&#xff0c;没有本地的 IDE 那么顺手。所以我想搭建一个本地类似 Playground 的环境&#xff0c;这样在学习 TypeScript 的过程中&#xff0c;可以更方…...

大模型(LLM)面试全解:主流架构、训练目标、涌现能力全面解析

系列文章目录 大模型&#xff08;LLMs&#xff09;基础面 01-大模型&#xff08;LLM&#xff09;面试全解&#xff1a;主流架构、训练目标、涌现能力全面解析 大模型&#xff08;LLMs&#xff09;进阶面 文章目录 系列文章目录大模型&#xff08;LLMs&#xff09;基础面一、目…...

入门嵌入式(七)——PWM

PWM 脉冲宽度调制PWM&#xff08;Pulse-Width Modulation&#xff09; 一组方波 周期 1ms 频率 1s / 周期 占空比 高电平/周期 数字信号&#xff1a;0/1高低电平 串口 IIC PWM都属于数字信号 模拟信号&#xff1a;电压值&#xff0c; 会受到电磁影响 ADC 模数转换 用于读取…...

Flutter 实现 列表滑动过程控件停靠效果 学习

实现一个 Flutter 应用程序&#xff0c;使用 Sliver 系列组件来创建具有滚动效果的复杂布局。使用 NestedScrollView 和 SliverPersistentHeader 来实现固定和动态的头部效果&#xff0c;以及一个可滚动的列表。 前置知识点学习 SingleTickerProviderStateMixin SingleTicker…...

STM32+WIFI获取网络时间+8位数码管显示+0.96OLED显

资料下载地址&#xff1a;STM32WIFI获取网络时间8位数码管显示0.96OLED 1、项目介绍 主控芯片STM32C8T6 接线&#xff1a;串口1&#xff1a;PA9 PA10 OELD &#xff1a;PB6 PB7 数码管使用&#xff1a;MAX7219 8位数码管 Max7219_pinCLK PAout(5) Max7219_pinC…...

Kivy App开发之UX控件Slider滑块

在app中可能会调节如音量,亮度等,可以使用Slider来实现,该控件调用方便,兼容性好,滑动平稳。在一些参数设置中,也可以用来调整数值。 支持水平和垂直方向,可以设置默认值,最小及最大值。 使用方法,需用引入Slider类,通过Slider类生成一个滑块并设置相关的样式后,再…...

STM32学习(十)

I2C模块内部结构 I2C&#xff08;Inter-Integrated Circuit&#xff09;模块是一种由Philips公司开发的二线式串行总线协议&#xff0c;用于短距离通信&#xff0c;允许多个设备共享相同的总线‌。 ‌硬件连接简单‌&#xff1a;I2C通信仅需要两条总线&#xff0c;即SCL&…...

何为“正则表达式”!

详细解释&#xff1a; ^&#xff1a;在JSON的正则表达式中&#xff0c;^表示匹配输入字符串的开始位置。这意味着正则表达式将从字符串的开头开始进行匹配&#xff0c;确保整个字符串符合后续的模式要求。例如&#xff0c;对于字符串"3.14"&#xff0c;正则表达式会…...

创建Java项目,并添加MyBatis包和驱动包

一 : Mybatis和jsp使用上,只有Dao层有区别 Mybatis 使用方法: 测试类的7步骤 1.读取核心配置文件 2.构建sql会话工厂 3.开启sql会话 4.获取mapper接口 5.调用相对应的增删改查方法 6.打印 7.关闭回话 /*** 用户列表* throws IOException*/Testpublic void roleList() throws IO…...

目标检测文献阅读-DETR:使用Transformer进行端到端目标检测

目录 摘要 Abstract 1 引言 2 DETR结构 2.1 Backbone 2.2 Encoder 2.3 Decoder 2.4 FFN 3 目标检测集合预测损失 3.1 二分图匹配损失 3.2 损失函数 总结 摘要 本周阅读的论文题目是《End-to-End Object Detection with Transformers》(使用Transformer进行端到端目…...

C#里对已经存在的文件进行压缩生成ZIP文件

先要对目录下所有文件获取到: private List<string> GetXDFiles(string dirPath){//获取目录中的所有文件string suffix = "*.txt"; // 要查找的文件后缀var files = Directory.GetFiles(dirPath, suffix, SearchOption.TopDirectoryOnly).ToList();return fi…...

单片机软件定时器V4.0

单片机软件定时器V4.0 用于单片机定时执行任务等&#xff0c;比如LED GPIO等定时控制&#xff0c;内置前后台工作模式 头文件有使用例子 #ifndef __SORFTIME_APP_H #define __SORFTIME_APP_H#ifdef __cplusplus extern "C" { #endif#include <stdint.h>// #…...

【嵌入式硬件】嵌入式显示屏接口

数字显示串行接口&#xff08;Digital Display Serial Interface&#xff09; SPI 不过多赘述。 I2C-bus interface 不过多赘述 MIPI DSI MIPI (Mobile Industry Processor Interface) Alliance, DSI (Display Serial Interface) 一般用于移动设备&#xff0c;下面是接口…...

vuedraggable 选项介绍

vuedraggable 是基于 SortableJS 的 Vue 组件&#xff0c;提供了丰富的选项来定制拖拽行为。以下是 vuedraggable 常用的选项和它们的详细说明&#xff1a; 常用选项介绍 group 配置拖拽分组。多个列表可以共享同一个分组&#xff0c;允许它们之间的项目互相拖拽。 group: { na…...

OpenAI CEO 奥特曼发长文《反思》

OpenAI CEO 奥特曼发长文《反思》 --- 引言&#xff1a;从 ChatGPT 到 AGI 的探索 ChatGPT 诞生仅一个多月&#xff0c;如今我们已经过渡到可以进行复杂推理的下一代模型。新年让人们陷入反思&#xff0c;我想分享一些个人想法&#xff0c;谈谈它迄今为止的发展&#xff0c;…...

Appium(一)--- 环境搭建

一、Android自动化环境搭建 1、JDK 必须1.8及以上(1) 安装&#xff1a;默认安装(2) 环境变量配置新建JAVA_HOME:安装路径新建CLASSPath%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar在path中增加&#xff1a;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin&#xff1b;(3) 验证…...

如何查看服务器上的MySQL/Redis等系统服务状态和列表

如果呢你知道系统服务名称&#xff0c;要看状态很简单&#xff1a; systemctl status server-name 比如 systemctl status nginxsystemctl status redis # 等 这是一个nginx的示例&#xff1a; 那问题是 当你不知道服务名称时该怎么办。举个例子&#xff0c;比如mysql在启动…...

多模态论文笔记——U-ViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍U-ViT的模型架构和实验细节&#xff0c;虽然没有后续的DiT在AIGC领域火爆&#xff0c;但为后来的研究奠定了基础&#xff0c;但其开创性的探索值得学习…...

08-1_队列的理论讲解

08-1_队列的理论讲解 队列概念理解 队列就是一个传送带,就是一个流水线 工人放入的数据, 放在传送带上,送给消费者, 消费者第一次拿到的数据,就是工人第一次放上的数据(类比队列,先进先出) freertos队列应用思考 队列对于消费者(取数据): (1)当队列没有数据, 消费者需要进入…...

HTML基础入门——简单网页页面

目录 一&#xff0c;网上转账电子账单 ​编辑 1&#xff0c;所利用到的标签 2&#xff0c;代码编写 3&#xff0c;运行结果 二&#xff0c;李白诗词 1&#xff0c;所用到的标签 2&#xff0c;照片的编辑 3&#xff0c;代码编写 4&#xff0c;运行结果 一&#xff0c;网…...

Proteus-8086调试汇编格式的一点心得

这阵子开始做汇编的微机实验&#xff08;微机原理与接口技术题解及实验指导&#xff0c;吴宁版本13章&#xff09;&#xff0c;中间出了挺多问题&#xff0c;解决后记录下。 先上电路图 用子电路来仿真发现仿真的时候子电路这块根本没有高低电平输出&#xff0c;只好把子电路拿…...

Python入门教程 —— 多任务

1.线程 1.1.线程安全问题 线程访问全局变量 import threading g_num = 0 def test(n):global g_numfor x in range(n):g_num += xg_num -= xprint(g_num)if __name__ == __main__:t1 = threading.Thread(target=test, args=(10,))t2 = threading.Thread(target=test, args=(…...

【笔记】算法记录

1、求一个数的素因子&#xff08;试除法&#xff09; // 获取一个数的所有素因子 set<int> getPrimeFactors(int num) {set<int> primeFactors;for (int i 2; i * i < num; i) {while (num % i 0) {primeFactors.insert(i);num / i;}}if (num > 1) {prime…...

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无 前端代码开源库 关于开源说明&#xff1a;企业服务-招聘信息管理系统-前端uniapp-系统前端开放源代码仅供学习-优雅草科技-目前优雅草科…...

【网络】计算机网络的分类 局域网 (LAN) 广域网 (WAN) 城域网 (MAN)个域网(PAN)

局域网是通过路由器接入广域网的 分布范围 局域网Local Area Network&#xff1a;小范围覆盖&#xff0c;速度高&#xff0c;延迟低(办公室&#xff0c;家庭&#xff0c;校园&#xff0c;网络) 广域网Wide Area Network 大范围覆盖&#xff0c;速度相对低&#xff0c;延迟高…...

DeepSeek:性能强劲的开源模型

deepseek 全新系列模型 DeepSeek-V3 首个版本上线并同步开源。登录官网 chat.deepseek.com 即可与最新版 V3 模型对话。 性能对齐海外领军闭源模型​ DeepSeek-V3 为自研 MoE 模型&#xff0c;671B 参数&#xff0c;激活 37B&#xff0c;在 14.8T token 上进行了预训练。 论…...

MySql 通过 LOAD DATA INFILE 导入大量数据

背景&#xff1a;要在本地Mysql导入几十万-百万条数据非常耗时&#xff0c;从网上找到通过load data infile 方式可以快速导入大量数据&#xff0c;就动手尝试了。结果就是不太适合复杂的格式数据导入&#xff0c;比如字段多&#xff0c;数据格式多等&#xff0c;但对于简单的表…...

Linux系统中解决端口占用问题

在日常的 Linux 系统管理和开发过程中&#xff0c;端口占用是一个常见且令人头疼的问题。无论是部署新服务、调试应用程序&#xff0c;还是进行系统维护&#xff0c;遇到端口被占用都可能导致服务无法正常启动或运行。本文将详细介绍在 Linux 系统中如何识别和解决端口占用问题…...

两种方式实现Kepware与PLC之间的心跳检测

两种方式实现Kepware与PLC之间的心跳检测 实现Kepware与PLC之间的心跳检测1.OPCUA 外挂程序2.Kepware Advanced Tag 实现Kepware与PLC之间的心跳检测 1.OPCUA 外挂程序 这是通过上位程序来触发心跳的一种机制&#xff0c;在C#中&#xff0c;可以利用OPC UAOPCAutodll的方式…...

使用DockerCompose部署服务

由于格式或图片解析问题&#xff0c;为了更好的阅读体验&#xff0c;可前往 阅读原文 以前我们总是用命令管理每个容器的启动、停止等等&#xff0c;若有多个容器时可能还存在启动优先级的问题&#xff0c;那就要等到指定的容器启动后再去启动另一个容器&#xff0c;对于整体的…...

第P5周-Pytorch实现运动鞋品牌识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: Pytorch &#xff08;二&#xff09;具体步骤 时间…...

react-quill 富文本组件编写和应用

index.tsx文件 import React, { useRef, useState } from react; import { Modal, Button } from antd; import RichEditor from ./RichEditor;const AnchorTouchHistory: React.FC () > {const editorRef useRef<any>(null);const [isModalVisible, setIsModalVis…...

因泰立科技激光车检器,高速公路的精准流量统计专家

高速公路自由流门架激光车检器&#xff0c;专为解决高速公路交通管理中的各种挑战而设计。该产品采用先进的激光测量技术&#xff0c;能够对动态通过的车辆进行高速动态扫描&#xff0c;通过测量物体表面点的反射距离&#xff0c;迅速换算成三维空间坐标&#xff0c;实现对车辆…...

windows中,git bash 使用conda命令

1、首先在Anaconda的安装路径如/Anaconda3/Scripts下&#xff0c;打开git bash窗口&#xff0c;然后输入下面的命令。 ./conda init bash 运行之后&#xff0c;会在用户目录下面生成.bash_profile文件&#xff0c;文件内容如下&#xff1a; # >>> conda initialize…...

《繁星路》V1.8.3(Build16632266)官方中文学习版

《繁星路》官方中文版https://pan.xunlei.com/s/VODae2_2Z3QyMF02I5y321uHA1?pwdqgsh# 作为一款星际模拟游戏&#xff0c;完美融合了硬科幻元素与基地建设玩法&#xff0c;体验改造行星的恢弘与壮阔。化身人工意识AMI&#xff0c;遵照基本指示推进火星改造的各项工作&#xf…...

vscode 无法使用npm, cmd命令行窗口可以正常执行

解决方法&#xff1a; 执行命令获得命令的位置 get-command npm 得到如下 然后删除或者修改 npm.ps1文件 让其不能使用就行。然后重启vscode即可。 pnpm 同理即可 另外加速源 国内镜像源&#xff08;淘宝&#xff09;&#xff1a; npm config set registry https://regist…...

html + css 顶部滚动通知栏示例

前言 在现代网页设计中&#xff0c;一个吸引人的顶部滚动通知栏不仅能够有效传达重要信息&#xff0c;还能提升用户体验。通过使用HTML和CSS&#xff0c;我们可以创建既美观又功能强大的组件&#xff0c;这些组件可以在不影响网站整体性能的情况下提供实时更新或紧急通知。 本…...