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

C/C++:内存管理

文章目录

  • 前言
  • 一、内存分区
    • 1. 内存划分情况
    • 2. 最大内存计算
  • 二、malloc/calloc/realloc 与 free
    • 1. malloc
    • 2. calloc
    • 3. realloc
    • 4. free
    • 5. 差异对比
    • 6. 失败处理
  • 三、内存分配题目
    • 1. 题目
    • 2. 内存区域划分
  • 四、C++内存管理方式
    • 1. new 与 delete
    • 2. new/delete操作内置类型
    • 3. new和delete操作自定义类型
  • 五、operator new与operator delete函数
    • 1. 原理
    • 2. 异常捕获方法
  • 六、new和delete的实现原理
    • 1. 内置类型
    • 2. 自定义类型
  • 七 、定位new表达式(placement-new)
  • 八、对比malloc/free和new/delete的区别
  • 总结


前言

今天我们来看C/C++中对于内存的管理
在这里插入图片描述


一、内存分区

1. 内存划分情况

在C/C++中,数据的存储位置取决于数据类型和作用域。通常分为以下几类:

  1. 栈(Stack)

    • 自动变量:局部变量、函数参数等会存储在栈上。当函数调用时,栈帧分配用于存储这些变量,函数结束后栈帧被销毁,变量随之消失。
    • 特点:栈内存分配快,自动管理,但空间有限(通常为几MB)。
  2. 堆(Heap)

    • 动态分配的内存:使用malloccalloc(C语言)或new(C++)分配的内存位于堆上。程序员需手动管理堆内存,使用free(C语言)或delete(C++)释放。
      • 特点:堆空间大,但分配速度较慢,且需要手动释放,易出现内存泄漏。
  3. 全局/静态区(Global/Static)

  • 全局变量:定义在函数外部的变量,作用域为整个程序,存储在全局/静态区。
  • 静态变量:使用static修饰的变量,即使定义在函数内,生命周期也是整个程序运行期间,存储在全局/静态区。
  • 特点:内存一直保留,直到程序结束。
  1. 常量区(Text Segment/ROData)

    • 字符串字面值:如"Hello, World!"这样的字符串存储在常量区,只读不可修改。
    • const修饰的变量:也可以存储在常量区,具体取决于编译器实现。
  2. 代码区(Code Segment)

  • 函数代码:编译后的程序代码(如函数体)存储在代码区,程序执行时从这里读取指令。

在这里插入图片描述


2. 最大内存计算

在32位(x86)和64位(x64)架构中,CPU的寻址能力决定了能够访问的最大内存空间。

  1. 32位(x86)架构

    • 寻址能力:在32位系统中,CPU使用32位地址(即4字节)来表示内存地址。
    • 最大寻址空间:32位地址可以表示的最大数值为 ( 2^{32} ) ,即 4,294,967,296 个地址(字节),也就是4GB内存空间。
    • 具体解释:CPU可以通过一个32位地址生成从0到(2^{32}-1)的地址,这意味着它能够访问最多4GB的内存。
  2. 64位(x64)架构

    • 寻址能力:在64位系统中,CPU使用64位地址(即8字节)来表示内存地址。
    • 理论最大寻址空间:64位地址理论上可以表示的最大数值为 ( 2^{64} ),即 18,446,744,073,709,551,616 个地址(字节),也就是 16 exabytes(EB) 的内存空间。
    • 实际情况:目前的操作系统和硬件并不会使用所有的64位地址位。现代操作系统和硬件会限制实际的可用内存寻址范围。例如,Windows 64位系统支持的最大内存一般为几TB到几十TB,具体取决于版本和硬件的限制。

在这里插入图片描述
在这里插入图片描述


二、malloc/calloc/realloc 与 free

在C语言中,malloccallocreallocfree 是动态内存管理的四个重要函数。它们在程序运行时负责分配、重新分配和释放内存。下面详细讲解它们的工作机制,并总结它们的区别。

1. malloc

  1. malloc (Memory Allocation)
    • 功能:分配指定大小的内存块。
    • 语法
      void* malloc(size_t size);
      
      其中,size 是要分配的内存大小(以字节为单位),返回值是指向已分配内存块的指针。如果分配失败,malloc 返回 NULL
    • 特点
      • malloc 分配的内存块中的数据不初始化,内容可能是随机的(内存中的残留数据)。
      • 适用于一次性分配特定大小的内存。
    • 示例
      int* ptr = (int*) malloc(10 * sizeof(int)); // 分配存储10个整数的内存
      

2. calloc

  1. calloc (Contiguous Allocation)
    • 功能:分配内存并初始化为零。
    • 语法
      void* calloc(size_t num, size_t size);
      
      其中,num 是要分配的元素个数,size 是每个元素的大小(以字节为单位)。返回指向内存块的指针,如果失败,返回 NULL
    • 特点
      • calloc 分配的内存会自动初始化为全零
      • 适用于需要多个连续内存块的情况,且希望这些内存块初始化为零。
    • 示例
      int* ptr = (int*) calloc(10, sizeof(int)); // 分配并初始化存储10个整数的内存
      

calloc 就相当于 malloc + memset
在这里插入图片描述


3. realloc

  1. realloc (Reallocation)
    • 功能:重新分配已分配内存的大小。
    • 语法
      void* realloc(void* ptr, size_t new_size);
      
      其中,ptr 是指向之前分配的内存块的指针,new_size 是新内存块的大小(以字节为单位)。realloc 返回指向新内存块的指针。
    • 特点
      • 用于扩展或缩小已经分配的内存块。
      • 如果需要扩展且原有内存块之后的空间不够,realloc在新位置分配内存,并复制旧数据,然后释放原有内存块;如果足够,则会在原位置上扩展。
      • 如果缩小内存块,多余的内存将被释放,但原始数据仍保留。
    • 示例
      int* ptr = (int*) realloc(ptr, 20 * sizeof(int)); // 将原来10个整数的内存扩展到20个
      

realloc扩容空间的做法:
在这里插入图片描述


4. free

  1. free (Memory Deallocation)
    • 功能:释放之前用 malloccallocrealloc 分配的内存。
    • 语法
      void free(void* ptr);
      
      其中,ptr 是指向需要释放的内存块的指针。
    • 特点
      • 必须为动态分配的内存显式调用 free,否则会导致内存泄漏(内存不会被释放,导致系统内存资源逐渐减少)。
      • 释放后,指针指向的内存不再有效,访问它会导致未定义行为(例如:访问已释放的内存可能引发段错误)。
    • 示例
      free(ptr); // 释放之前分配的内存
      

5. 差异对比

总结:malloccallocreallocfree 的区别

函数名主要功能内存初始化参数用途
malloc分配指定大小的内存块无(内容未初始化)size:分配的字节数动态分配内存
calloc分配并初始化内存全部初始化为0num:元素个数
size:每个元素的大小
分配多个内存块并初始化为零
realloc重新分配已分配的内存原数据保留ptr:原内存指针
new_size:新大小
扩展或缩小现有内存
free释放已分配的内存无(释放操作)ptr:待释放的内存指针释放不再需要的内存

区别和应用场景:

  1. 内存初始化malloc 分配的内存不初始化,calloc 分配的内存会被初始化为 0。
  2. 用法不同
    • 使用 malloc 适合一次性分配已知大小的内存。
    • 使用 calloc 适合分配多个元素的内存块,并且需要初始化为零。
    • 使用 realloc 适合在内存不足或需要调整内存时动态调整内存大小。
  3. 释放内存:所有通过 malloccallocrealloc 分配的内存,最后都必须通过 free 释放,否则会造成内存泄漏。

6. 失败处理

对于如果开空间失败了怎么办

//realloc, calloc同理
int* ptr = (int*) malloc(10 * sizeof(int));
if (ptr == NULL) {// 内存分配失败的处理逻辑perror("malloc fail!");exit(-1);
}

三、内存分配题目

1. 题目

我们先来看下面的一段代码和相关问题:

int globalVar = 1;static int staticGlobalVar = 1;void Test(){static int staticVar = 1;int localVar = 1;int num1[10] = {1, 2, 3, 4};char char2[] = "abcd";char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof (int)*4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);free (ptr1);free (ptr3);}
  1. 选择题:

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

globalVar在哪里?C staticGlobalVar在哪里?C

staticVar在哪里?C localVar在哪里?A

num1 在哪里?A

分析:

globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区

staticVar静态局部变量在静态区 localVar局部变量在栈区

num1局部变量在栈区

char2在哪里?A *char2在哪里?A

pChar3在哪里?A *pChar3在哪里?D

ptr1在哪里?A *ptr1在哪里?B

分析:

char2局部变量在栈区

char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上

pChar3局部变量在栈区 *pChar3得到的是字符串常量字符在代码段

ptr1局部变量在栈区 *ptr1得到的是动态申请空间的数据在堆区
在这里插入图片描述

  1. 填空题:

sizeof(num1) = 40;//数组大小,10个整形数据一共40字节

sizeof(char2) = 5;//包括\0的空间

strlen(char2) = 4;//不包括\0的长度

sizeof(pChar3) = 4;//pChar3为指针

strlen(pChar3) = 4;//字符串“abcd”的长度,不包括\0的长度

sizeof(ptr1) = 4;//ptr1是指针


2. 内存区域划分

在这里插入图片描述


    • 又称堆栈,用于存储非静态局部变量、函数参数、返回值等,栈是向下增长的。
  1. 内存映射段

    • 高效的I/O映射方式,用于加载共享的动态内存库。通过系统接口创建共享内存,进行进程间通信。(如果还没学到这部分内容,现在只需了解即可。)
    • 用于程序运行时的动态内存分配,堆是向上增长的。
  2. 数据段

    • 存储全局变量和静态变量。
  3. 代码段

    • 包含可执行代码和只读常量。

四、C++内存管理方式

1. new 与 delete

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦(主要是在对类),因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。


2. new/delete操作内置类型

这段代码展示了在C++中如何使用newdelete进行动态内存分配和释放。下面逐行讲解代码的含义:

int main()
{// 动态申请一个int类型的空间int* ptr4 = new int;
  • 动态申请单个int类型的内存
    • 使用new int分配一个int类型的内存空间,并返回其地址,赋值给指针ptr4
    • 此时,内存中的值未初始化,可能是一个随机值。
    // 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);
  • 动态申请单个int类型的内存并初始化
    • 使用new int(10)分配内存并将其初始化为10。指针ptr5指向这个内存位置。
    // 动态申请10个int类型的空间int* ptr6 = new int[3];
  • 动态申请数组
    • 使用new int[3]分配一个能够存储3个int的数组。内存中的值未初始化,因此每个元素的值都是随机的。
    int* ptr7 = new int[3]{ 1,2,3 };
  • 动态申请数组并初始化
    • 使用new int[3]{ 1,2,3 }分配一个能够存储3个int的数组,并将第一个元素初始化为1,第二个元素初始化为2,第三个元素初始化为3。未指定的元素会被初始化为0。
    int* ptr8 = new int[5]{ 1,2,3 };
  • 动态申请更大数组并部分初始化
    • 使用new int[5]{ 1,2,3 }分配一个能够存储5个int的数组,前3个元素分别被初始化为1、2、3,后2个元素自动初始化为0。
    delete ptr4;delete ptr5;
  • 释放单个变量的内存
    • 使用delete释放由new分配的单个int类型的内存。ptr4ptr5所指向的内存空间被释放。
    delete[] ptr6;delete[] ptr7;delete[] ptr8;
  • 释放数组的内存
    • 使用delete[]释放动态分配的数组内存。ptr6ptr7ptr8分别指向的数组内存被正确释放。
    • 注意:内存泄漏
      使用 delete 释放一个数组分配的内存可能会导致内存泄漏(如:释放这个数组不加[]),delete ptr6;。delete 只会释放第一个元素的内存,后续元素的内存没有被正确释放,这会使得程序中的内存使用逐渐增加。

3. new和delete操作自定义类型

现在我们有一个A类:

class A
{
public:A(int a = 1): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main() {// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间// 还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A)); // 使用malloc申请内存A* p2 = new A(1); // 使用new申请内存,并调用构造函数
  • 内存分配
    • malloc:分配内存给一个 A 类型的对象(p1),但不会调用构造函数。
    • new A(1):分配内存并调用构造函数,初始化 _a 为 1,并将指针赋值给 p2
    free(p1); // 使用free释放内存delete p2; // 使用delete释放内存
  • 内存释放
    • free(p1):释放通过 malloc 分配的内存,不会调用析构函数。
    • delete p2:释放通过 new 分配的内存,同时会调用析构函数,打印析构信息。
    // 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int; // C++free(p3); // 释放p3delete p4; // 释放p4
  • 内置类型的内存管理
    • mallocfree 的使用与 newdelete 处理内置类型(如 int)几乎一样。
    • malloc 不会初始化内存,而 new 会为内置类型分配内存。
    A* p5 = (A*)malloc(sizeof(A)*10); // 使用malloc申请10个A对象的内存A* p6 = new A[10]; // 使用new申请10个A对象的内存
  • 数组的内存分配
    • malloc:分配内存给10个 A 类型的对象,但不会调用构造函数。
    • new A[10]:分配内存并调用构造函数,为每个对象初始化。
    free(p5); // 释放通过malloc分配的内存delete[] p6; // 释放通过new[]分配的内存,调用每个对象的析构函数
  • 数组内存释放
    • free(p5):释放10个 A 类型对象的内存。
    • delete[] p6:释放通过 new[] 分配的内存,确保调用每个对象的析构函数。

总结

  • newdelete vs mallocfree
    • newdelete 用于分配和释放自定义类型的内存时,会自动调用构造函数和析构函数,而 mallocfree 只进行内存分配和释放,不调用构造和析构函数。

五、operator new与operator delete函数

1. 原理

newdelete是用户进行动态内存申请和释放的操作符,operator new operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。


2. 异常捕获方法

#include <iostream>
#include <exception>
using namespace std;class A {
public:A(int a = 0) : _a(a) {cout << "A constructed: " << _a << endl;}~A() {cout << "A destructed." << endl;}private:int _a;
};void Func() {int* p1 = new int[1024 * 1024 * 100]; // 试图分配内存cout << p1 << endl;int* p2 = new int[1024 * 1024 * 100]; // 试图分配内存cout << p2 << endl;int* p3 = new int[1024 * 1024 * 100]; // 试图分配内存cout << p3 << endl;int* p4 = new int[1024 * 1024 * 100]; // 试图分配内存cout << p4 << endl;int* p5 = new int[1024 * 1024 * 100]; // 试图分配内存cout << p5 << endl;
}int main() {try {Func(); // 调用函数以分配内存} catch (const exception& e) {cout << "Memory allocation failed: " << e.what() << endl; // 捕获异常并打印信息}return 0;
}

六、new和delete的实现原理

1. 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,
不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。


2. 自定义类型

newdelete 原理

操作原理说明
new
1. 调用 operator new通过调用 operator new 函数申请足够的内存空间。
2. 执行构造函数在申请的内存空间上执行构造函数,完成对象的构造并返回指向对象的指针。
delete
1. 执行析构函数在对象的内存空间上执行析构函数,清理对象中占用的资源。
2. 调用 operator delete通过调用 operator delete 函数释放对象所占用的内存空间。
new T[N]
1. 调用 operator new[]调用 operator new[] 函数,通过实际调用 operator new 函数申请 N 个对象所需的内存空间。
2. 执行 N 次构造函数在申请的内存空间上执行 N 次构造函数,构造 N 个对象。
delete[]
1. 执行 N 次析构函数在释放的对象空间上执行 N 次析构函数,清理 N 个对象中占用的资源。
2. 调用 operator delete[]调用 operator delete[] 函数释放内存空间,实际在 operator delete[] 中调用 operator delete 来释放内存。

七 、定位new表达式(placement-new)

使用这个的理由是,我们对一个已经存在的对象,不能在外面显示调用它的构造函数,但是析构函数可以。
为了解决这个问题,引入定位new表达式。

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
// 定位new/replacement new
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没//有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;
}

八、对比malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放

总结

到这里,内存管理的东西就结束了,谢谢大家~

在这里插入图片描述

相关文章:

C/C++:内存管理

文章目录 前言一、内存分区1. 内存划分情况2. 最大内存计算 二、malloc/calloc/realloc 与 free1. malloc2. calloc3. realloc4. free5. 差异对比6. 失败处理 三、内存分配题目1. 题目2. 内存区域划分 四、C内存管理方式1. new 与 delete2. new/delete操作内置类型3. new和dele…...

jmeter学习(4)提取器

同线程组https://blog.csdn.net/vikeyyyy/article/details/80437530 不同线程组 在JMeter中&#xff0c;正则表达式提取的参数可以跨线程组使用。 通过使用Beanshell后置处理器和属性设置函数&#xff0c;可以将提取的参数设置为全局变量&#xff0c;从而在多个线程组之间共享…...

移动端的每日任务,golang后端数据库应该怎么设计

推荐学习文档 golang应用级os框架&#xff0c;欢迎stargolang应用级os框架使用案例&#xff0c;欢迎star案例&#xff1a;基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识&#xff0c;这里有免费的golang学习笔…...

1、Spring Boot 3.x 集成 Eureka Server/Client

一、前言 基于 Spring Boot 3.x 版本开发&#xff0c;因为 Spring Boot 3.x 暂时没有正式发布&#xff0c;所以很少有 Spring Boot 3.x 开发的项目&#xff0c;自己也很想了踩踩坑&#xff0c;看看 Spring Boot 3.x 与 2.x 有什么区别。自己与记录一下在 Spring Boot 3.x 过程…...

Vue根实例、实例总结

在Vue.js框架中&#xff0c;根实例和实例扮演着至关重要的角色。以下是对Vue根实例和实例的总结&#xff1a; Vue根实例 定义与创建&#xff1a; Vue根实例是Vue.js应用的核心。每个Vue应用都是通过用Vue函数创建一个新的Vue实例开始的&#xff0c;这个实例被称为根实例。根实…...

微服务架构:Spring Cloud的服务注册与发现、配置管理、服务网关、熔断器、分布式追踪

微服务架构是一种将应用程序构建为一组小型、自治的服务的方法&#xff0c;每个服务都运行在其独立的进程中&#xff0c;服务间通过轻量级通信机制&#xff08;通常是HTTP API&#xff09;进行通信。Spring Cloud是一套基于Spring Boot的微服务解决方案&#xff0c;它提供了一系…...

Spring Boot实现的大学生就业市场解决方案

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…...

Ubuntu上安装Git:简单步骤指南

Git是目前世界上最流行的版本控制系统&#xff0c;广泛用于软件开发中。无论你是开发者还是版本控制的新手&#xff0c;Git都是你不可或缺的工具。本文将为你介绍如何在Ubuntu操作系统上安装Git。 什么是Git&#xff1f; Git是一个开源的分布式版本控制系统&#xff0c;由Lin…...

新闻推荐系统:Spring Boot的架构优势

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…...

谷歌收录批量查询,谷歌收录批量查询的简单方法

谷歌收录批量查询是网站管理员和SEO优化人员常见的需求&#xff0c;以下提供几种简单且高效的批量查询方法&#xff1a; 一、使用Google Search Console&#xff08;谷歌搜索控制台&#xff09; 注册并验证网站&#xff1a; 首先&#xff0c;确保你已经在Google Search Conso…...

HarmonyOS NEXT应用开发(一、打造最好用的网络通信模块组件)

随着HarmonyOS NEXT 的发布&#xff0c;越来越多的开发者开始关注如何在这个新平台上高效地进行应用开发。其中网络通信模块的封装尤为关键。纵观HarmonyOS的众多三方网络库及封装&#xff0c;竟没有一个简单好用的。虽然有个axios的鸿蒙版&#xff0c;但有点儿重了也不是很好用…...

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT JetBrains2024&#xff08;IntelliJ IDEA、PhpStorm、RubyMine、Rider……&#xff09;安装包Anaconda Miniconda安装.condarc 文件配置镜像源查看conda的配置和源(channel)自定义conda虚拟环境路径conda常用命…...

如何选择合适的BI工具及集成

目录 1 BI工具介绍 1.1 BI工具介绍 1.2 大数据平台与BI工具的集成 2 选择BI工具的考虑因素 2.1 可视化与分析能力 2.2 易用性与学习曲线 2.3 数据源与连接性 2.4 定制化与扩展性 3 案例分析 3.1 案例一:某零售行业的应用 3.2 案例二:某金融企业的应用 3.3 案例三:…...

STM32的串行外设接口SPI

一、SPI简介 1.SPI总线特点 &#xff08;1&#xff09;四条通信线 SPI需要SCK、MISO、MOSI、NSS四条通信线来完成数据传输 &#xff0c;每增加一个从机&#xff0c;多一条NSS通信线。 &#xff08;2&#xff09;多主多从 SPI总线允许有多个主机和多个从机。 &#xff08;3&…...

函数重载

一、概念 C 允许在同一作用域中存在几个功能类似的同名函数&#xff0c;但这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同 int Add(int left, int right) {return leftright; }double Add(double left, double right) {return leftright; }long Add(long left, l…...

单例模式:Python中的“独一无二”模式

引言 单例模式是一种常用的设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这种模式在需要控制资源消耗、管理共享资源或者协调系统组件时非常有用。例如&#xff0c;数据库连接、配置文件管理、日志记录等场景。 基础语法介绍 单例模式的…...

C++和OpenGL实现3D游戏编程【连载12】——游戏中音效的使用

🔥C++和OpenGL实现3D游戏编程【目录】 1、游戏中音效的使用 前面我们实现了图片纹理的显示功能,是不是感觉到非常的简单。那么今天我们就继续说下游戏声音的实现。音效也是游戏的灵魂,只有搭配了美妙动听的音效以后,游戏才能令人耳目一新,与玩家产生良好的效果。 音效文…...

Hive数仓操作(八)

一、Hive中的分桶表 1. 分桶表的概念 分桶表是Hive中一种用于提升查询效率的表类型。分桶指的是根据指定列的哈希值将数据划分到不同的文件&#xff08;桶&#xff09;中。 2. 分桶表的原理 哈希分桶&#xff1a;根据分桶列计算哈希值&#xff0c;对哈希值取模&#xff0c;将…...

【C++打怪之路Lv6】-- 内存管理

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;C打怪之路&#xff0c;python从入门到精通&#xff0c;数据结构&#xff0c;C语言&#xff0c;C语言题集&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分82)&#…...

408知识点自检(二)

一、细节题 边界对齐长度是由什么决定的&#xff1f;64位计算机边界按几字节对齐&#xff1f;单周期cpu、多周期cpu、基本流水线cpu、超标量cpu的cpi分别是多少&#xff1f;中断的处理优先级和响应优先级分别由谁决定&#xff1f;动态分区管理需要用什么重定位方式&#xff1f…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

python可视化:俄乌战争时间线关键节点与深层原因

俄乌战争时间线可视化分析&#xff1a;关键节点与深层原因 俄乌战争是21世纪欧洲最具影响力的地缘政治冲突之一&#xff0c;自2022年2月爆发以来已持续超过3年。 本文将通过Python可视化工具&#xff0c;系统分析这场战争的时间线、关键节点及其背后的深层原因&#xff0c;全面…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件&#xff0c;这次有个需求是要兼容xp环境&#xff0c;xp上chrome最高支持到49&#xff0c;虽然说iframe或者embed都可以实现预览pdf&#xff0c;但为了后续的定制化需求&#xff0c;还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...

【Pandas】pandas DataFrame dropna

Pandas2.2 DataFrame Missing data handling 方法描述DataFrame.fillna([value, method, axis, …])用于填充 DataFrame 中的缺失值&#xff08;NaN&#xff09;DataFrame.backfill(*[, axis, inplace, …])用于**使用后向填充&#xff08;即“下一个有效观测值”&#xff09…...

ABB馈线保护 REJ601 BD446NN1XG

配电网基本量程数字继电器 REJ601是一种专用馈线保护继电器&#xff0c;用于保护一次和二次配电网络中的公用事业和工业电力系统。该继电器在一个单元中提供了保护和监控功能的优化组合&#xff0c;具有同类产品中最佳的性能和可用性。 REJ601是一种专用馈线保护继电器&#xf…...