C++

重要概念

1、栈内存

函数内部声明的变量占用的内存

2、堆内存

程序未使用的内存,可用于动态分配内存

动态内存

程序在运行时动态分配内存,有时我们不知道我们需要分配多少内存,而是到了运行时才能知道,这时就需要动态分配内存,而其它的分配方式叫静态分配内存。面对需求灵活性高的程序,动态内存的地位极为重要。

若需要查看应用场景示例,请在本文右侧导航栏跳转至“应用场景”。


正题

newdelete 运算符

new 运算符

用于为变量申请内存,其格式为

1
<var-name> = new <data-type>; 

例:

1
2
char *c[];
c = new char[20];

即为char指针变量c申请缓冲区大小为20个字节的内存空间,如果在实际开发中,数据大小超过了该缓冲区就会导致缓冲区溢出报错。

还有第二种用法:

1
int *num = new num();

写了括号但不写其值,这种用法即是开辟一个内存空间并初始化为0

delete 运算符

delete 运算符通常用于将已经使用完了的内存或不用的内存释放,根据需求合理释放内存是一个很好的习惯,不及时释放内存在小型项目中数据较少也许一时半会不会出现太大的问题,但是在大型项目开发中不释放内存会导致客户端可用内存越来越少,直至程序崩溃或是其它更严重的后果。

使用方法

1
delete <<return-type> *var-name>;

示例:

1
2
3
4
5
6
7
8
9
#include <iostream>
int main(){
int *p = NULL; // 声明指针变量并初始化内存为NULL
p = new int; // 为该变量申请内存
*p = 114514; // 为该变量赋值
std::cout << *p << std::endl; // 输出该变量的值
delete p; // 使用完了,释放内存
return 0;
}
注意

delete运算符在操作数组变量时有些特别

1
2
3
4
5
6
7
8
//错误示范
int main(){
char *p[];
p = new char[20];
*p = {1,1,4,5,1,4};
delete p; //<————报错,语法错误
return 0;
}
1
2
3
4
5
6
7
8
//正确示范
int main(){
char *p[];
p = new char[20];
*p = {1,1,4,5,1,4};
delete [] p; // 编译运行成功,释放了p指向的数组内存
return 0;
}

在对多维数组进行内存释放时,也有些不同,需要进行数组内存迭代释放。

这种写法是错误的:

1
2
int **arr[5][7] = { /*...*/ };
delete [][] arr;
1
2
int **arr[5][7] = { /*...*/ };
delete [5][7] arr;
1
2
int **arr[5][7] = { /*...*/ };
delete [][7] arr;

正确写法:

1
2
3
4
5
int **arr[5][7] = { /*...*/ };
for(int i = 0; i < 7; i++){
delete [] arr[i];
}
delete [] arr;

大于二维的数组需要多次for循环嵌套删除数组:

1
2
3
4
5
6
7
8
9
10
int ****arr[5][7][9] = { /*...*/ };
for(int i = 0; i < 5; i++){
for(int j = 0; j < 7; j++){
delete [] arr[i][j];
}
}
for(int i = 0; i < 9; i++){
delete [] arr[i];
}
delete [] arr;

delete ptrdelete [] ptr的区别

delete ptr用于释放ptr所指向的内存

delete [] ptr用于释放ptr指向的内存,还逐一调用数组中每个对象的 destructor

C语言

动态内存管理

注:同样适用于C++,但在一些地方与C有别。

C语言内存通过指针变量进行管理,普通变量由程序自行管理,当变量出了其作用域后自动销毁,该程序退出后所有指针变量和普通变量占用内存一并销毁。同时必须注意,在分配内存的时候,C++必须要做强制类型转换,C语言不作硬性的要求

重要头文件:<stdlib.h>

该头文件下4个重要函数

1
2
3
4
- void *calloc(int num, int size);
- void free(void *address);
- void *malloc(int num);
- void *realloc(void *address, int newsize);

void *calloc(int num, int size);

作用:在内存中动态分配 num 个长度为 size 的连续空间,并且将每一字节都初始化为 0。该函数与malloc函数类似,但是不同之处在于 calloc 在分配后还会将每一字节都进行一次初始化操作,而malloc函数则只是进行分配而不负责初始化,在性能方面,calloc函数比不上malloc,在分配内存的时候并不是每次都需要进行初始化,因此选择malloc函数进行内存分配是最佳的选择。

void *malloc(int num);

作用:在堆区分配一块指定大小的内存空间,用来存放数据,该块内存在函数执行后不会被初始化,它们的值是未知的。

void free(void *address);

作用:该函数释放 address 指向的内存块,释放的是动态分配的内存空间。虽然程序退出后会销毁程序所有已分配的内存,但是内存在不再使用的时候,进行销毁是一个好习惯。

语法:

1
free(<var-name>);

void realloc(void *address, int newsize);

作用:该函数重新分配内存,把内存扩展或缩小到 newsize

例如,我想把内存块扩展到200*sizeof()个单位:

1
<var-name> = <var-data-type*>realloc(<var-name>, 200 * sizeof(<var-data-type>));

应用场景

假如你是李华,今年7岁,你在新学期的自我介绍活动中准备上台发表陈词。

若要在程序中构建出李华这一个人和发表陈词这件事,则必须包含李华的姓名与年龄还有性别,且它们都是已知量,我们还知道它们所占字符数,但是李华的陈词是不确定的,假定它是由神经网络生成的陈词,但这必须是在程序运行后才能知道李华的陈词的所占字符数,因此,就可以使用动态内存分配来解决这个问题了,因为动态内存分配是在程序运行时根据一定条件来分配相应的内存。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h> //<---重要!
//...
void main(){
char name[20];
int age;
char sex[20];
char *explain[];
strcpy(name, "LiHua");
age = 7;
strcpy(sex, "Male");
//statementLen变量为李华的陈词句子长度变量,其量会随着程序的运行而改变,此处只作简单示例,实际开发并非如此
if(statementLen > 200){
explain = (char*)malloc(200 * sizeof(char));
// ^^^^^ ^^^^^^^^^^^^^^^^^^
// 强制类型转换 此处等价于 char c[200]
// 若是C++必须转换
// 变量是什么类型就要强转为什么类型
if(explain != NULL){ //检测是否是空指针
strcpy(explain, aiStatement); // aiStatement为ai生成的语句,既然开辟了内存空间,就将其内容拷贝进内存空间里
printf("大家好,我是%s, 今年%d岁,一个%s孩子,%s", name, age,sex,explain); //输出
//...
}else{
//...
}
}else if(statementLen > 400){ //根据需求改变
explain = (char*)realloc(explain, 400 * sizeof(char));
//...
}else if(statementLen > 800){
explain = (char*)realloc(explain, 800 * sizeof(char));
//...
}
}