【C++入门到精通】C++入门 —— 内存管理(new函数的讲解)

news2024/11/14 20:22:53

目录

一、C/C++内存分布

1. 栈(Stack)

2. 堆(Heap)

3. 全局区/静态区(Global Area/Static Area)

4. 常量区(Constant Area)

5. 代码区(Code Area)

二、C语言中动态内存管理方式

1. malloc函数

 2. calloc函数

3. realloc函数

4. free函数

三、C++中动态内存管理

四、operator new与operator delete函数

⭕operator new

⭕operator delete

五、new和delete的实现原理

1.内置类型

2.自定义类型

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

总结

malloc/free和new/delete的区别

什么是内存泄漏,内存泄漏的危害


 前言

        前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象。也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C和C++比较重要的知识点——内存管理。下面话不多说坐稳扶好咱们要开车了😍

一、C/C++内存分布

         C/C++的内存分布较为复杂,大概分为五个部分它们分别是:(Stack)、(Heap)、全局区/静态区(Global Area/Static Area)、常量区(Constant Area)、代码区(Code Area)下面我会分别介绍他们各自的属性。

1. 栈(Stack)

🍟栈是用来存储函数的局部变量、函数参数和函数调用的返回地址等信息的内存空间。
🍟栈空间由编译器自动管理,它的分配和释放是自动进行的,不需要程序员手动操作。
🍟每当一个函数被调用时,编译器会在栈上为该函数分配一块特定大小的空间,称为函数帧或活动记录(Activation Record)。
🍟栈是一种高效的存储方式,由于栈上的内存是连续分配的,因此访问速度较快。然而,栈的大小是有限的,过多的栈帧可能导致栈溢出的错误。

2. 堆(Heap)

🍁堆是用来动态分配内存的空间,通过使用malloc、free、new、delete等操作来分配和释放内存。
🍁堆的分配是手动进行的,程序员需要显式地分配和释放内存。
🍁堆是一种灵活的存储方式,允许动态调整分配的内存大小。
🍁堆上的内存分配通常是不连续的,因此访问速度稍慢于栈
🍁如果使用不当,堆上的内存可能导致内存泄漏或内存碎片化等问题

3. 全局区/静态区(Global Area/Static Area)

🍔全局区用来存储全局变量和静态变量
🍔全局变量在程序开始执行时分配,在程序结束时被释放,因此它们的生命周期与整个程序的运行周期相同
🍔静态变量有不同的存储位置和生命周期,具体取决于其作用域和存储类型(例如静态局部变量、静态全局变量)。
🍔全局区的内存空间在程序启动时由操作系统分配,并在程序退出时由操作系统释放

4. 常量区(Constant Area)

🍪常量区用来存储常量数据,例如字符串字面量和其他常量
🍪常量区的内存通常是只读的,因此程序不能对其进行修改
🍪常量区的数据在程序运行期间保持不变

5. 代码区(Code Area)

🥝代码区用来存储程序的机器指令,也称为可执行代码
🥝代码区通常是只读的,存储程序的执行逻辑
🥝代码区的内存空间在程序启动时被分配,并且在程序运行期间保持不变

         🚨注意:具体的内存布局和分配方式可能因编译器、操作系统和硬件平台的不同而有所差异。此外,在多线程和多进程的情况下,还需要考虑线程栈、共享内存等特殊情况。

二、C语言中动态内存管理方式

1. malloc函数

  • malloc 函数用于动态地分配指定大小的内存空间。
  • 它接受一个参数,即需要分配的内存大小(以字节为单位),并返回所分配内存的起始地址。
    int ptr = (int)malloc(sizeof(int));
  • malloc 函数在堆上找到足够大的连续内存空间进行分配,如果找不到足够大的连续空间,则返回 NULL 
  • 分配的内存是未初始化的,即其中的数据是不确定的,需要手动初始化。
#include <stdio.h>
#include <stdlib.h>

int main() {
    int size;
    int* arr;

    printf("Enter the size of the array: ");
    scanf("%d", &size);

    // 使用malloc函数动态分配内存
    arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }

    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }

    printf("The elements you entered are: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }

    // 释放已分配的内存
    free(arr);

    return 0;
}

上面的程序实现了以下步骤:

  1. 用户输入数组的大小。
  2. 使用 malloc 函数动态分配足够大的内存空间来存储整数数组。
  3. 检查内存分配是否成功,如果分配失败,则输出错误信息并结束程序。
  4. 用户输入数组的元素。
  5. 打印用户输入的数组元素。
  6. 使用 free 函数释放分配的内存空间。

 2. calloc函数

  •  calloc 函数用于动态地分配指定数量和大小的内存空间,并将其初始化为0
  • 它接受两个参数,第一个是需要分配的元素数量,第二个是每个元素的大小(以字节为单位)。
    int ptr = (int)calloc(5, sizeof(int));
  •  calloc 函数在堆上找到足够大的内存空间进行分配,并将所有字节初始化为0
  • 分配的内存是初始化过的,不需要手动初始化。
#include <stdio.h>
#include <stdlib.h>

int main() {
    int size;
    int* arr;

    printf("Enter the size of the array: ");
    scanf("%d", &size);

    // 使用calloc函数动态分配内存,并将内存初始化为0
    arr = (int*)calloc(size, sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }

    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }

    printf("The elements you entered are: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }

    // 释放已分配的内存
    free(arr);

    return 0;
}

        上面的代码功能跟上面 malloc 函数的示例表达的功能一样这里不做过多的解释,记得在完成使用动态分配的内存后,使用 free 函数释放已分配的内存,以免出现内存泄漏。

3. realloc函数

  •  realloc 函数用于重新调整之前分配的内存空间的大小。
  • 它接受两个参数,第一个是之前分配内存的起始地址,第二个是需要调整的新大小(以字节为单位)
    ptr = (int*)realloc(ptr, 10 * sizeof(int));
  •  realloc 函数将尝试在原始内存空间上重新调整大小,如果成功,则返回调整后的内存地址;如果原始内存空间不够大或者其他错误,则返回 NULL 
  • 若新大小大于原始大小,则新增的字节未初始化;若新大小小于原始大小,则超出新大小的部分将被丢弃。
#include <stdio.h>
#include <stdlib.h>

int main() {
    int size;
    int* arr;

    printf("Enter the size of the array: ");
    scanf("%d", &size);

    // 使用malloc函数动态分配内存
    arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed!");
        return 1;
    }

    printf("Enter %d elements:\n", size);
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }

    // 重新分配内存,将数组大小调整为10
    int newSize = 10;
    int* newArr = (int*)realloc(arr, newSize * sizeof(int));
    if (newArr == NULL) {
        printf("Memory reallocation failed!");
        free(arr);  // 释放之前分配的内存
        return 1;
    }

    // 更新指针arr的引用
    arr = newArr;

    // 打印数组元素
    printf("The elements you entered are: ");
    for (int i = 0; i < newSize; i++) {
        printf("%d ", arr[i]);
    }

    // 释放分配的内存
    free(arr);

    return 0;
}

         首先使用 malloc 函数动态分配内存,并在之后的 realloc 操作中进行了适当的错误处理。如果 realloc 函数返回 NULL ,表示内存调整失败,此时会输出错误信息并释放之前分配的内存。如果 realloc 函数成功,则将新分配的内存地址赋给 arr 指针,并继续使用调整后的内存。在最后,使用 free 函数释放分配的内存空间,并返回 0 表示程序正常结束

4. free函数

  •  free 函数用于释放之前动态分配的内存空间
  • 传入 free 函数的参数是之前分配的内存块的起始地址。
  • 调用free函数将释放所指定的内存空间,使其可供之后的malloccallocrealloc等函数重新使用。

        这些动态内存管理函数提供了灵活的内存分配和释放能力,可以根据需要动态调整内存的大小。然而,需要确保正确使用,避免内存泄漏和野指针等问题。建议在每次分配内存后检查分配是否成功,以及在适当的时候使用 free 函数释放不再需要的内存。

void Test ()
{
    int* p1 = (int*) malloc(sizeof(int));
    free(p1);


    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);

    // 这里需要free(p2)吗?  答:不用
    free(p3 );
}

        上面的代码中不需要 free(p2) 因为当调用realloc函数时,如果成功调整了内存大小,那么原始内存区域的内容将被复制到新分配的内存,并且原始的内存空间会被释放。因此,在这种情况下,不需要再手动调用 free(p2) 来释放原始的内存空间,因为realloc函数已经负责处理了

三、C++中动态内存管理

        C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过 newdelete 操作符进行动态内存管理new 操作符在C++中用于在堆上动态分配内存,可以用于分配单个对象、对象数组以及动态创建对象。它调用对象的构造函数进行初始化,并需要使用 delete 操作符手动释放分配的内存。

new 操作符的一般语法为:

ptr = new T;
delete ptr; // 释放已分配的内存

其中,T 是要分配的对象的类型,ptr 是一个指针变量,用于存储分配对象的地址。

int* ptr = new int;  // 分配一个int类型的内存
*ptr = 10;  // 向分配的内存存储数据

// 动态申请一个int类型的空间并初始化为10
int* ptr1 = new int(10);

delete ptr; // 释放已分配的内存
delete ptr1; // 释放已分配的内存

在上述示例中,new int 用于分配一个 int 类型的内存,并返回一个指向该内存的指针,然后将值 10 存储到该内存中。

如果需要分配一个数组,可以使用new[]操作符:

int size = 5;
int* arr = new int[size]; // 分配一个包含5个int元素的数组内存
for (int i = 0; i < size; i++) {
    arr[i] = i;
}
delete[] arr; // 释放已分配的数组内存

        在上面的代码中,new int[size] 用于分配包含5个 int 元素的数组,并返回指向该数组首元素的指针。然后,通过循环将每个元素赋值为其索引。

需要注意以下几点:

  • 使用new分配的内存位于堆上,直到使用delete手动释放或对象超出作用域时才会被释放。
  • 当使用new操作符分配内存时,会调用对象的构造函数来初始化该对象。
  • 分配的内存应使用相应的delete或delete[]操作符进行释放,以避免内存泄漏。
  • new操作符分配内存失败时,会抛出std::bad_alloc异常,因此需要进行错误处理。

除了上面提到的使用new来分配内存,还可以使用new操作符来动态创建对象并初始化,例如:

class MyClass {
public:
    MyClass(int value) : m_value(value) {
        std::cout << "Constructor called." << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor called." << std::endl;
    }

    void PrintValue() {
        std::cout << "Value: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    MyClass* obj = new MyClass(42);  // 动态创建MyClass对象,并通过构造函数进行初始化
    obj->PrintValue();
    delete obj;  // 手动释放内存,调用析构函数
    return 0;
}

        在上面的代码中,通过 new 动态创建一个 MyClass 对象,并调用构造函数进行初始化。通过指针访问对象的成员函数,并最后使用 delete 释放所分配的内存。在申请自定义类型的空间时,new 会调用构造函数,delete 会调用析构函数,而 mallocfree 不会

        在使用 new / deletenew[] / delete[] 进行动态内存管理时,应该准确匹配每个 new 操作符和相应的 delete 操作符,或者每个 new[] 操作符和相应的 delete[] 操作符。不正确的匹配可能会导致内存泄漏或未定义行为。

四、operator new与operator delete函数

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

⭕operator new

        该函数实际通过 malloc 来申请空间,当 malloc 申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。

void* operator new(size_t size) {
    // 调用底层的内存分配函数,例如 malloc
    void* ptr = malloc(size);
    if (ptr == nullptr) {
        // 内存分配失败,抛出异常
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete(void* ptr) noexcept {
    // 调用底层的内存释放函数,例如 free
    free(ptr);
}

int main() {
    int* ptr = new int;
    *ptr = 10;
    delete ptr;
    return 0;
}

        在上面的代码中,operator new 函数调用了底层的  malloc 函数来分配内存,并在分配失败时抛出 std::bad_alloc 异常。 operator delete 函数调用了底层的   free 函数来释放内存。上面的这些只是概念性的代码,实际的底层实现可能更加复杂,并可能涉及到处理内存对齐、内存池、内存追踪等操作。具体的底层代码会因编译器、操作系统和编译选项的不同而有所差异。

        总的来说,operator new 实际也是通过 malloc 来申请空间,如果 malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。

⭕operator delete

该函数最终是通过free来释放空间的,operator delete 函数的底层逻辑

  • operator delete函数负责释放通过operator new分配的内存。它会调用底层的内存释放函数,如freefree函数是操作系统提供的用于释放内存的函数。
  • operator delete函数将传入的内存指针作为参数,并将其传递给底层的free函数来释放内存。
  • operator delete函数不会关心所释放的内存大小。因此,释放内存时必须确保传入的内存指针是通过对应的operator new分配的,并且内存大小与分配时的大小匹配。
  • C++标准库还提供了operator delete[]函数,用于释放通过operator new[]分配的数组内存。
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 delete的示例:

void operator delete(void* ptr) noexcept {
    // 自定义内存释放逻辑,例如使用其他的内存释放函数
    myMemoryFreeFunction(ptr);
}

int* ptr = new int;
delete ptr;  // 使用自定义的operator delete进行内存释放

        通过重载operator delete,可以使用自定义的内存释放逻辑来代替默认的内存释放策略。这对于使用自定义的内存分配方案或特定的内存管理需求非常有用。

需要注意的问题有以下几点:

  • operator new和 operator delete函数是全局函数,可以在全局范围内进行重载。
  • 可以根据需要重载不同的版本,在重载时可以提供额外的参数来进行定制化的内存分配或释放操作。
  • 在重载过程中,要遵循相应的内存对齐规则和语义,以确保内存分配和释放的正确性。
  • 使用自定义的operator newoperator delete函数时要注意内存的一致性和正确释放,以避免内存泄漏或未定义行为。

五、new和delete的实现原理

1.内置类型

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

2.自定义类型

  • new的原理

1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造

  •  delete的原理

1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间

  •  new T[N]的原理

1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
2. 在申请的空间上执行N次构造函数 

  •  delete[]的原理

 1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

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

        定位new表达式(Placement new)是C++中的一种特殊形式的new表达式,用于在已经分配的内存区域上构造对象。它可以在指定的内存地址上调用对象的构造函数,而不是分配新的内存来创建对象。

定位new表达式的语法如下:

new (ptr) Type(arguments);

        其中,ptr 是一个指向已分配内存的指针, Type 是要构造的对象类型, arguments 是传递给对象构造函数的参数。

        使用定位new表达式时,会在给定的内存地址上调用对象的构造函数,将对象在该地址上构造出来。这意味着对象的内存由用户预先分配,而不是由new操作符动态分配。由此,定位new表达式允许在指定的内存位置创建对象,适用于特定的内存分配需求。以下是一个使用定位new表达式的示例:

#include <iostream>

class MyClass {
public:
    MyClass(int value) : m_value(value) {
        std::cout << "Constructor called" << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }

    void printValue() {
        std::cout << "Value: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    // 分配内存
    void* memory = operator new(sizeof(MyClass));

    // 在已分配的内存上构造对象
    MyClass* obj = new (memory) MyClass(42);

    // 调用对象的成员函数
    obj->printValue();

    // 调用对象的析构函数
    obj->~MyClass();

    // 释放内存
    operator delete(memory);

    return 0;
}

        在上述示例中,我们首先使用 operator new 手动分配了一块内存,然后通过定位new表达式在这个内存地址上构造了一个MyClass对象。然后,我们可以通过对象指针调用成员函数和析构函数。最后,使用 operator delete 释放了这块内存。

        注意:定位new表达式要求用户自行管理内存的分配和释放,确保在构造和析构期间正确处理生命周期。同时,使用定位new表达式时必须保证分配的内存空间足够容纳对象,并且类型对齐正确。

        定位new表达式是C++中的一种特殊形式的new表达式,用于在已经分配的内存区域上构造对象。它使用分配的内存地址来调用对象的构造函数,允许在指定的内存位置创建对象,适用于特定的内存分配需求。使用定位new表达式时,要确保正确管理内存的分配和释放,并确保类型对齐正确。

总结

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++、算法和编程的奥秘。祝您生活愉快,排便顺畅!


 

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

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

相关文章

【HAL库】STM32CubeMX开发----STM32F407----LAN8720A----移植FreeModbus实现ModbusTCP

前言 本次实验以 STM32F407VET6 芯片为MCU&#xff0c;使用 25MHz 外部时钟源。 以太网PHY层芯片为 LAN8720A&#xff0c;移植FreeModbus实现ModbusTCP网口通信。 具体内容参考文章&#xff1a;【HAL库】STM32CubeMX开发----STM32F407----ETHLAN8720ALWIP----ping通 本次移植…

基于开源模型搭建实时人脸识别系统(三):人脸关键点、对齐模型概览与模型选型

续 基于开源模型搭建实时人脸识别系统&#xff08;二&#xff09;&#xff1a;人脸检测概览与模型选型_CodingInCV的博客-CSDN博客 摘要 人脸对齐&#xff08;face alignment&#xff09;或者人脸关键点&#xff08;face alignment&#xff09;是定位人脸上的关键点&#xff…

chatGLM 本地部署(windows+linux)

chatGLM算是个相对友好的模型&#xff0c;支持中英文双语的对话交流&#xff0c;清华出的 我的教程无需特别的网络设置&#xff0c;不过部分情况因为国内网络速度慢&#xff0c;需要反复重复 chatGLM github地址 一、硬件需求 N卡8G显存以上&#xff0c;最好16G以上&#xff…

redis 集群 1:李代桃僵 —— Sentinel

目前我们讲的 Redis 还只是主从方案&#xff0c;最终一致性。读者们可思考过&#xff0c;如果主节点凌晨 3 点突发宕机怎么办&#xff1f;就坐等运维从床上爬起来&#xff0c;然后手工进行从主切换&#xff0c;再通知所有的程序把地址统统改一遍重新上线么&#xff1f;毫无疑问…

电路暂态过程

本文仅提取了课程的部分内容&#xff0c;原视频课程如下&#xff1a;姜三勇《电工学》暂态过程 电路暂态过程——产生的原因&#xff1a; 1、内部原因&#xff1a;电路内部含有储能元件&#xff08;如&#xff1a;电容、电感&#xff09;&#xff0c;其中存储的能量不能发生改…

vscode如何退出/切换 github 账号

退出/切换 github 账号 左下角点击头像按钮&#xff0c;选择注销&#xff0c;然后再重新登录

GateOne任意文件读取

人的生命力&#xff0c;是在痛苦的煎熬中强大起来的。 简介 GateOne 是一款使用 HTML5 技术编写的网页版 SSH 终端模拟器。基于现代的 HTML5技术&#xff0c;无需任何浏览器插件、支持多个 SSH 进程、可以嵌入到其他任意应用程序中、支持使用 JavaScript&#xff0c;Python 甚…

SaaS销售打法:常规有效的四种SaaS营销策略,落地执行增长突破

在外面常规的认知中&#xff1a;SaaS营销与常规的营销其实没有什么不同&#xff0c;都在遵循着传统的推广和销售策略&#xff0c;简单来说就是将自己的产品卖给更多的用户&#xff0c;从而换取利润。 SaaS营销策略 但是SaaS产品与其他产品不同的地方在于它本身是没有实体的&am…

如何基于eBPF实现跨语言、无侵入的流量录制?

测试是产品发布上线的一个重要环节&#xff0c;但随着业务规模和复杂度不断提高&#xff0c;每次上线需要回归的功能越来越多&#xff0c;给测试工作带来了巨大的压力。在这样的大背景下&#xff0c;越来越多的团队开始使用流量回放对服务进行回归测试。 在建设流量回放能力之前…

一台电脑给另外一台电脑共享网络

这里写自定义目录标题 有网的电脑上操作一根网线连接两台电脑没网的电脑上 有网的电脑上操作 右键->属性->共享 如同选择以太网&#xff0c;勾选。确认。 一根网线连接两台电脑 没网的电脑上 没网的电脑为mips&麒麟V10 新增个网络配置ww&#xff0c;设置如下。 …

产品设计中的小体验:带来大问题解决之道

在激烈的市场竞争中&#xff0c;产品的体验设计已成为区分优劣的重要标志。用户不仅仅关注产品的核心功能&#xff0c;更重视产品在使用过程中的舒适度、易用性和情感体验。产品设计中的细节体验&#xff0c;看似微不足道&#xff0c;却往往能带来意想不到的效果。这是因为&…

线上服务挂了 3 分钟

在一个风和日丽的下午&#xff0c;刚打算饮茶&#xff0c;线上就开始报警了&#xff0c;一看情况网关报 500 了。。 网关&#xff08;用的是Spring Cloud Gateway&#xff09;挂了可还行&#xff0c;这可是对外的们&#xff0c;门没了岂不是所有请求都进不来了&#xff01; 说…

Linux 系统编程 开篇/ 文件的打开/创建

从本节开始学习关于Linux系统编程的知识&#xff01; 学习Linux的系统编程有非常多的知识点&#xff0c;在应用层面&#xff0c;很重要的一点就是学习如何“用代码操作文件来实现文件创建&#xff0c;打开&#xff0c;编辑等自动化执行” 那如何自动化实现对文件的创建&#…

成本控制策略:加强企业安全

我们生活在一个不确定的时代。大多数经济学家预测&#xff0c;今年全球经济将继续放缓&#xff0c;亚太地区当然也不会逆势而上。 在供应链问题、大规模裁员、高通胀和高利率之间&#xff0c;我们毫不奇怪地看到大多数公司和行业采取谨慎态度&#xff0c;战略、增长计划和预算…

使用docker安装wordpress详细教程及出现数据库无法连接问题解决方法

1.获取wordpress镜像 docker pull wordpress 2.创建wordpress 的容器 a.创建wordpress的文件镜像卷文件夹 mkdir wordpress b.创建wordpress镜像 docker run --name wp -p8080:80 -v /home/wordpress/:/var/www/html -d wordpress c.查看容器运行情况 3.在本地或者其他服务器创…

AutoDL从0到1搭建stable-diffusion-webui

前言 AI绘画当前非常的火爆&#xff0c;随着Stable diffusion&#xff0c;Midjourney的出现将AI绘画推到顶端&#xff0c;各大行业均受其影响&#xff0c;离我们最近的AI绘画当属Stable diffusion&#xff0c;可本地化部署&#xff0c;只需电脑配备显卡即可完成AI绘画工作&…

Go语言并发编程(千锋教育)

Go语言并发编程&#xff08;千锋教育&#xff09; 视频地址&#xff1a;https://www.bilibili.com/video/BV1t541147Bc?p14 作者B站&#xff1a;https://space.bilibili.com/353694001 源代码&#xff1a;https://github.com/rubyhan1314/go_goroutine 1、基本概念 1.1、…

宋老板教我做人--背后少说别人

宋老板教我做人——背后少说别人 2000年&#xff5e;2004年间发生的事 让我很难忘&#xff0c;让我长记性 趣讲大白话&#xff1a;是不是传说中的&#xff0c;发自内心的善良&#xff1f; 【趣讲信息科技246期】 **************************** 真实故事1&#xff1a; 2000年5月…

Embedding入门介绍以及为什么Embedding在大语言模型中很重要

Embeddings技术简介及其历史概要 在机器学习和自然语言处理中&#xff0c;embedding是指将高维度的数据&#xff08;例如文字、图片、音频&#xff09;映射到低维度空间的过程。embedding向量通常是一个由实数构成的向量&#xff0c;它将输入的数据表示成一个连续的数值空间中…

【python】绘图代码模板

【python】绘图代码模板 pandas.DataFrame.plot( )画图函数Seaborn绘图 -数据可视化必备导入数据集可视化统计关系使用Seaborn绘制散点图抖动图箱线图小提琴图Pointplot群图 可视化数据集的分布绘制单变量分布柱状图直方图 绘制双变量分布Hex图KDE 图可视化数据集中的成对关系 …