探索C语言的内存魔法:动态内存管理解析
✨✨ 欢迎大家来到贝蒂大讲堂✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:C语言学习
贝蒂的主页:Betty‘s blog
1. 静态开辟内存
通过前面的学习,我们已经掌握了两种开辟内存的方法,分别是:
#include<stdio.h>
int main()
{int val = 20; //在栈空间上开辟四个字节char arr[10] = { 0 }; //在栈空间上开辟10个字节的连续空间return 0;
}
但是静态开辟的空间明显有两个缺陷:
- 空间开辟⼤⼩是固定的。
- 数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整。
2. 动态内存
为了解决静态内存开辟的内存空间固定的问题,C语言引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。
2.1 动态内存开辟函数
(1) malloc函数
头文件#include <stdlib.h>
声明:void* malloc (size_t size);
- size – 内存块的大小,以字节为单位
- 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
作用:向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针
- 如果开辟成功,则返回⼀个指向开辟好空间的指针。
- 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
返回值:返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
补充打印错误信息函数:perror()
头文件:#include <stdio.h>
声明:void perror(const char *str)
- str – 这是 C 字符串,包含了一个自定义消息,将显示在原本的错误消息之前。
作用:把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。
返回值:无返回值。
下列是malloc与perror的具体使用方法:
int main()
{int* arr = (int*)malloc(sizeof(int) * 10);//开辟十个大小为整型的空间//返回类型强转为int*if (arr == NULL)//如果开辟失败{perror("malloc fail: ");//打印错误信息return 1;//直接返回}int i = 0;for (i = 0; i < 10; i++)//存入数据{arr[i] = i;}for (i = 0; i < 10; i++)//打印数据{printf("%d ", arr[i]);}return 0;
}
输出结果:

监视观察:

- 动态内存的数据存放在堆区
(2) calloc函数
头文件:#include <stdlib.h>
声明:void *calloc(size_t nitems, size_t size)
- nitems – 要被分配的元素个数。
- size – 元素的大小。
作用: 分配所需的内存空间,并返回一个指向它的指针
返回值:该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
- malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
下列是calloc的使用实例:
int main()
{int* arr = (int*)calloc(10, sizeof(int));//开辟十个大小为整型的空间//返回类型强转为int*if (arr == NULL)//如果开辟失败{perror("calloc fail: ");//打印错误信息return 1;//直接返回}return 0;
}
calloc的初始化观察:

(3) realloc函数
- 头文件:#include <stdlib.h>
- 声明:void *realloc(void *ptr, size_t size)
- ptr – 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size – 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
- 作用:尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
- 返回值:该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
- 有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时候内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤⼩的调整。
- realloc扩容机制:
- 本地扩容:原有空间之后有⾜够⼤的空间,直接在原有内存之后直接追加空间,原来空间的数据不发⽣变化。

- 异地扩容:原有空间之后没有⾜够⼤的空间,在堆空间上另找⼀个合适⼤⼩的连续空间。将新增数据与原本数据拷贝过来,并自动释放原来空间。

下列是realloc的具体使用方法:
int main()
{int* arr = (int*)calloc(10, sizeof(int));//开辟十个大小为整型的空间//返回类型强转为int*if (arr == NULL)//如果开辟失败{perror("calloc fail: ");//打印错误信息return 1;//直接返回}//继续新增空间int* tmp = (int*)realloc(arr, sizeof(int) * 15);//不用arr是为了防止开辟失败,被至为NULLif (tmp == NULL)//如果开辟失败{perror("calloc fail: ");//打印错误信息return 1;//直接返回}arr = tmp;return 0;}

- 新增内存较小时一般是在原有基础上新增空间。两者地址相同。
int* tmp = (int*)realloc(arr, sizeof(int) * 100);//新增内存较大时

- 新增内存较大时则会重新开辟一段空间,将原来的空间释放。两者地址不同。
2.2 动态内存释放函数
动态内存开辟的空间并不像静态开辟内存的空间会随着一段程序的结束而回收,这时就需要我们手动回收,否则就会造成内存泄漏。
- **内存泄漏(Memory Leak)**是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
- 头文件:#include <stdlib.h>
- 声明:void free(void *ptr)
- ptr – 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
- 作用:释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
- 返回值:该函数不返回任何值。
下面使用free函数的实例:
int main()
{int* arr = (int*)calloc(10, sizeof(int));//开辟十个大小为整型的空间//返回类型强转为int*if (arr == NULL)//如果开辟失败{perror("calloc fail: ");//打印错误信息return 1;//直接返回}//继续新增空间int* tmp = (int*)realloc(arr, sizeof(int) * 100);if (tmp == NULL)//如果开辟失败{perror("calloc fail: ");//打印错误信息return 1;//直接返回}arr = tmp;free(arr);//释放arr所指向的内存arr = NULL;return 0;}
- 释放完之后记得将arr置为NULL,否则arr指向一段已经回收的空间会变成野指针。
2.3 常见内存分布
⼀般我们在学习C/C++语⾔的时候,我们会关注内存中的三个区域:栈区、 堆区、静态区。
-
局部变量与函数参数是放在内存的栈区,
-
全局变量,static修饰的变量是放在内存的静态区。
-
堆区是⽤来动态内存管理的。
具体分布如下图:

3. 动态内存的常见错误
动态内存开辟就像指针一样,一不小心就会酿成大错,以下介绍了一些常见的内存开辟错误:
3.1 对NULL指针的解引用
void test()
{int* p = (int*)malloc(INT_MAX / 4);*p = 20; //如果p的值是NULL,就会有问题free(p);
}
- INT_MAX是一个宏定义,他表示整型的最大值,值为2147483647。
- 当malloc申请的空间太大时存在失败的情况,失败返回NULL指针。
- 而系统无法访问NULL指针指向的地址,这时编译器会报一个警告:

改正方法:
void test()
{int* p = (int*)malloc(INT_MAX / 4);if (NULL == p){perror("malloc fail: ");//打印错误信息return 1;}*p = 20;free(p);p = NULL;
}
- 这时就体现判断是否为空指针的重要性了
3.2 对动态开辟空间的越界访问
void test()
{int i = 0;int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){perror("malloc fail: ");//打印错误信息return 1;//直接返回}for (i = 0; i <= 10; i++){*(p + i) = i; //当i是10的时候越界访问}free(p);p=NULL;
}
- malloc只申请了十个整型大小的空间。
- for循环循环了十一次,越界访问,错误信息如下:

改正方法:
void test()
{int i = 0;int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){perror("malloc fail: ");//打印错误信息return 1;//直接返回}for (i = 0; i < 10; i++){*(p + i) = i; //当i是10的时候越界访问}free(p);p = NULL;
}
3.3 对非动态开辟内存使用free释放
void test()
{int a = 10;int* p = &a;free(p);p=NULL;//ok?
}
- free()只能释放有动态内存开辟在堆上的空间。
- p指向的空间是静态内存开辟的,无法释放,释放就会出错:

改正方法:
void test()
{int a = 10;int* p = &a;
}
- 静态内存开辟的空间并不需要释放。
3.4 使⽤free释放⼀块动态开辟内存的⼀部分
void test()
{int* p = (int*)malloc(100);p++;free(p); //p不再指向动态内存的起始位置p = NULL;
}
- p++跳过一个整型大小的空间。
- free()释放p只会释放当前位置开始之后的空间,有一个整型大小的空间未被释放,造成内存泄漏。

改正方法:
void test()
{int* p = (int*)malloc(100);free(p); p = NULL;
}
- 不能随意改变p指向的位置,开辟多少内存就释放多少内存。
3.5 对同⼀块动态内存多次释放
void test()
{int* p = (int*)malloc(100);free(p);free(p); //重复释放
}
- p已经被释放归还给操作系统,但是此时p还指向该内存,是一个野指针。
- 再次释放p就会出现内存出错问题。

改正方法:
void test()
{int* p = (int*)malloc(100);free(p);p = NULL;
}
- 释放内存之后记得将其置为空指针,这样再次free空指针就不会进行任何操作。
3.6 动态开辟内存忘记释放(内存泄漏)
void test()
{int* p = (int*)malloc(100);if (NULL != p){*p = 20;}//内存泄漏
}int main()
{test();
}
- 当我们动态内存申请空间之后必须手动将其释放,不会就会出现内存泄漏的问题。
改正方法:
void test()
{int* p = (int*)malloc(100);if (NULL != p){*p = 20;}free(p);p = NULL;
}
- 每次使用完动态内存开辟空间之后记得释放内存。
4. 相关笔试题
4.1 题目一
void GetMemory(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}//请问运⾏Test函数会有什么样的结果?
这段程序有两个经典错误:
- **内存非法访问:**我们知道传值调用时,形参只是实参的临时拷贝,对形参的改变无法影响实参,这时str仍是空指针,而strcpy拷贝会对空指针进行解引用操作,对NULL指针解引用会出错!


- **内存泄漏:**在GetMemory()函数内部动态申请了100字节的空间,因为p随着函数结束而被销毁,所以已经再也找不到该空间,会造成内存泄漏。
改正方法:
- 我们要想改变str就需要传址调用,而str本身就是个指针变量,传指针变量的地址需要二级指针来接收。
- 使用完之后必须释放内存。
void GetMemory(char** p)
{*p = (char*)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "hello world");printf(str);// 释放free(str);str = NULL;
}
4.2 题目二
char* GetMemory(void)
{char p[] = "hello world";return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}
- 这段程序是经典的野指针问题,局部变量出了作用就会销毁归还给操作系统,而str还能指向这块空间就会形成野指针。

改正方法:
- 因为只有存放在栈区的值才会被销毁,所以我们将其放在其他区域如:静态区,而放在静态区有两种方法:static修饰与常量字符串。
const char* GetMemory1(void)
{const char* p = "hello world";return p;
}char* GetMemory2(void){static char p[] = "hello world";return p;}void Test(void)
{char* str = NULL;str = GetMemory1();printf(str);printf("\n");str = GetMemory2();printf(str);
}int main()
{Test();return 0;
}
输出结果:

4.3 题目三
void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}
void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}
//请问运⾏Test函数会有什么样的结果?
- 这又是一个经典的内存泄漏问题——p开辟出内存未被释放。
改正方法:
void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);free(str);str = NULL;
}
4.4 题目四
void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf(str);}
}
//请问运⾏Test函数会有什么样的结果?
- 这也是个经典野指针问题,str所开辟的空间已经归还给了操作系统,这时再将world拷贝进str就会出错。
改正方法:
- 归还内存之后随手将其值为NULL指针,后续语句就不会进行。
void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);str = NULL;if (str != NULL){strcpy(str, "world");printf(str);}
}
5. 柔性数组
5.1 柔性数组是什么
C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫作柔性数组,例如:
typedef struct st_type
{int i;int a[0]; //柔性数组成员
}type_a;
有些编译器会报错⽆法编译可以改成:
typedef struct st_type
{int i;int a[]; //柔性数组成员
}type_a;
- 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
- 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。
5.2 柔性数组的大小
依靠我们结构体学过得内存对齐的原则,我们可以计算结构体的大小。
typedef struct st_type
{int i;int a[0]; //柔性数组成员
}type_a;
int main()
{printf("%d\n", sizeof(type_a)); return 0;
}
输出结果:

- 从上述可知柔性数组成员是不计入结构体大小的。
5.3 柔性数组的使用
柔性数组的使用与结构体使用十分类似,具体使用如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{int i;int a[]; //柔性数组成员
}type_a;
int main()
{int i = 0;type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));//包含柔性数组成员的结构⽤**malloc()函数**进⾏内存的动态分配,// 并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。p->i = 100;for (i = 0; i < 100; i++)//存放数据{p->a[i] = i;}free(p);return 0;
}

5.4 模拟实现柔性数组
先开辟一个结构体大小,在开辟一个数组的大小。
柔性数组成员的空间都是malloc开辟的,所以模拟的柔性数组也需要malloc开辟。
具体实施如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{int i;int* p_a;
}type_a;
int main()
{//先开辟一个结构体大小type_a* p = (type_a*)malloc(sizeof(type_a));p->i = 100;//在开辟一个数组大小p->p_a = (int*)malloc(p->i * sizeof(int));for (int i = 0; i < 100; i++){p->p_a[i] = i;}//释放空间free(p->p_a);p->p_a = NULL;free(p);p = NULL;return 0;
}

5.5 柔性数组的优势
通过与模拟的柔性数组对比,我们可以看出柔性数组的优势:
- 便内存释放:如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,容易造成内存泄漏。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉
- 这样有利于访问速度: 连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。
相关文章:
探索C语言的内存魔法:动态内存管理解析
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 1. 静态开辟内存 通过前面的学习,我们已经掌握了两种开辟内存的方…...
2023年全国职业院校技能大赛软件测试赛题第3套
2023年全国职业院校技能大赛 软件测试赛题第3套 赛项名称: 软件测试 英文名称: Software Testing 赛项编号: GZ034 归属产业: 电子与信息大类 …...
【数据分享】1929-2023年全球站点的逐日降水量数据(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,说到常用的降水数据,最详细的降水数据是具体到气象监测站点的降水数据! 有关气象指标的监测站点数据,之前我们分享过1929-2023年全…...
SpringBoot WebSocket客户端与服务端一对一收发信息
依赖 <!--websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>配置类 Configuration public class WebSocketConfig {Bean //方法返回值交…...
MinGW/MSYS/GCC/GNU/MSVC/Clang/LLVM都是什么
MinGW(Minimalist GNU for Windows): MinGW(Minimalist GNU for Windows)是一个用于Windows平台的开发工具集,它提供了一组用于编译和构建应用程序的工具和库。它的目标是在Windows环境下提供一个类Unix的…...
9.0 Zookeeper 节点特性
本章节介绍一下 zookeeper 的节点特性和简单使用场景,正是由于这些节点特性的存在使 zookeeper 开发出不同的场景应用。 1、同一级节点 key 名称是唯一的 实例: $ ls / $ create /runoob 2 已存在 /runoob 节点,再次创建会提示已经存在。 …...
VUEX项目场景
VUEX项目场景 一、登录状态存储 登录页面代码 <template><div><input v-model"username" type"text" placeholder"Username"><input v-model"password" type"password" placeholder"Password&…...
vue+springboot前后端视频文件等的上传与展示(基于七牛云)
前言:在初步说明完成功能之前,我会把重要的部分说明下。后续我会细化。 vue视频文件上传 其实这里和图片这些文件就是一样的。因为上传只是把我们想在云端展示的文件按等传输到云端的bucket。然后方便网站去请求引用。 有人问我我就说明下。这种东西无…...
ClickHouse--02--安装
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 安装官网 ;[https://clickhouse.com/docs/zh/getting-started/install](https://clickhouse.com/docs/zh/getting-started/install)![在这里插入图片描述…...
【学网攻】 第(23)节 -- PPP协议
系列文章目录 目录 系列文章目录 文章目录 前言 一、PPP协议是什么? 二、实验 1.引入 实验目的 实验背景你是某公司的网络管理员,现在需要与另一个公司进行通信,需要你配置PPP协议保证双方发送的人是真正的而非黑客 技术原理 实验步骤新建Pack…...
Rust方法自动解引用测试,总结和补充
// 定义一个结构体MyBox,包含一个原始指针 struct MyBox<T>(T);// 方法调用 . 操作,对方法的self 进行加& &mut * 还有 无大小转换 trait MyTrait {fn test0(mut self, x: &i32) where Self: Sized {println!("test0 :{}", …...
备战蓝桥杯---动态规划之经典背包问题
看题: 我们令f[i][j]为前i个物品放满容量为j的背包的最大价值。 f[i][j]max(f[i-1][j],f[i-1][j-c[i]]w[i]); 我们开始全副成负无穷。f[0][0]0;最后循环最后一行求max; 负无穷:0xc0c0c0c0;正无穷:0x3f3f3f3f 下面是v12,n6的图示ÿ…...
Go语言每日一练——链表篇(八)
传送门 牛客面试笔试必刷101题 ----------------两个链表的第一个公共结点 题目以及解析 题目 解题代码及解析 解析 这一道题使用的还是双指针算法,我们先求出两个链表的长度差n,然后定义快慢指针,让快指针先走n步,最后快慢指…...
跟着cherno手搓游戏引擎【23】项目维护、2D引擎之前的一些准备
项目维护: 修改文件结构: 头文件自己改改就好了 创建2DRendererLayer: Sandbox2D.h: #pragma once #include "YOTO.h" class Sandbox2D :public YOTO::Layer {public:Sandbox2D();virtual ~Sandbox2D() default;virtual void O…...
Redis(十三)缓存双写一致性策略
文章目录 概述示例 缓存双写一致性缓存按照操作来分,细分2种读写缓存:同步直写策略读写缓存:异步缓写策略双检加锁策略 数据库和缓存一致性更新策略先更新数据库,再更新缓存先更新缓存,再更新数据库先删除缓存…...
7 scala的类构造器
在创建对象的时候,需要调用类的构造器。Scala 提供了主构造器和辅助构造器。 1 主构造器 与 Java 一样,如果我们没有特别定义,那么 Scala 提供的默认构造器是没有参数的。 我们可以在类名后,指定构造器的参数列表,列…...
如何在 Mac 上恢复永久删除的文件:有效方法
您是否错误地从 Mac 中删除了某个文件,并且确信它已经永远消失了?好吧,你可能错了。即使您认为已永久删除计算机上的数据,仍有可能将其恢复。 在本文中,您将了解如何在 Mac 上恢复永久删除的文件,并了解增…...
Web后端开发:事务与AOP
事务管理 在学习数据库时,讲到:事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求,要么同时成功,要么同时失败。 事务的操作主要有三…...
[word] word如何打印背景和图片? #微信#其他#经验分享
word如何打印背景和图片? 日常办公中会经常要打印文件的,其实在文档的打印中也是有很多技巧的,可以按照自己的需求设定,下面给大家分享word如何打印背景和图片,一起来看看吧! 1、打印背景和图片 在默认的…...
Maven - 编译报错:程序包 XXX 不存在(多模块项目)
问题描述 编译报错:程序包 XXX 不存在(多模块项目) 原因分析 检查依赖模块 pom 文件,看是不是引入了如下插件 <plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-pl…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

