目录
基本知识框架
课堂笔记
内存操作的意义
可以动态开辟内存空间
关于开辟内存空间,按照以往的开辟方式:
int a = 5;
char str[6] = "Hello";
有两个特点:
- 对于变量,空间开辟的大小是确定
- 对于数组,在声明时数组的大小必须是确定的,因为编译器需要此依据来为数组开辟内存空间
我们开辟出的空间都是要事先确定的,而实际使用中,我们会有这样的需求,有些变量或数组的大小需要在程序运行后才确定,所以内存操作的意义就在于能够开辟动态地开辟内存空间
内存操作函数
malloc
void *malloc(size_t size);
功能:在内存中申请指定size大小的空间
返回值:如果申请成功则返回该内存空间的首地址,如果申请失败,返回NULL
注意:
- 使用malloc申请空间时要注意判断是否申请成功
- size的值如果为0,C语言中对于这样的情况是未定义的,最终结果取决于编译器
- malloc申请出来的内存空间,不会被初始化
- malloc申请出来的内存空间,大小以字节计算
calloc
void *calloc(size_t num, size_t size);
功能:在内存中申请连续num块指定size大小的空间
返回值:如果申请成功则返回内存空间的首地址,如果申请失败,返回NULL
注意:
- calloc申请出来的内存空间,会被初始化成0
- calloc申请出来的内存空间,大小以字节计算
realloc
void *realloc(void *ptr, size_t size)
功能:重新分配(变大 / 变小)之前已经申请的内存空间
返回:如果分配成功则返回重新分配内存空间的首地址,如果分配失败,返回NULL
注意:
- 使用realloc扩大内存空间的容量时,要保证地址后的后有足够的内存空间余量。如果当前要扩容的地址之后没有足够的余量,那么realloc会另外在有富余空间的地方进行扩容,并返回扩容空间后的地址,而不是返回你要扩容的地址
- realloc操作保留源地址指向空间中的数据,即使是出现上一种扩容空间不足的情况,也会讲数据重新复制到新空间
- 传入的参数包括要重新分配空间内存块的首地址ptr,以及要重新分配大小size
free
void free(void *ptr)
功能:释放已被分配内存空间
返回:无返回值
注意:
- 指针ptr指向要释放的内存空间
- 指针ptr不要指向非动态开辟的内存,这样的情况是未定义的
- 当指针ptr的值为NULL时,不会有任何效果
- 当分配的内存块较大时,并且不再使用时,要使用free函数去释放内存空间
memcpy
void *memcpy(void *dest, void *src, unsigned int count);
功能:拷贝内存空间,将源地址src指向空间拷贝count个字节的内容到目标地址dest指向的空间
返回值:返回拷贝完成后目标地址的指针dest
注意:
- 内存拷贝的字节数由count决定
- 当count>sizeof(src)时,会在末尾补上’\0’
- 源地址与目标地址指向的空间不能重叠
memmove
void *memmove(void *dest, void *src, unsigned int count);
功能:移动(拷贝)内存空间
返回值:同memcpy
注意:
- 和memcpy一样的功能,同样是拷贝内存空间
- memmove可以处理地址指向空间重叠的问题,注意源地址src只想的空间可能会被改变
memset
void *memset(void *buffer, int c, int count);
功能:初始化内存空间,将buffer指向的内存空间中的前count 个字节的内容初始化为整形数据c
返回值:初始化后的内存空间
注意:
- 一般用于给数组、字符串等类型赋值
memcmp
int memcmp(void *buf1, void *buf2, unsigned int count);
功能:比较buffer1和buffer2指向内存空间的前count个字节
返回值:比较结果,有符号整型数
注意:
- 功能上其实与strncmp差不多,都是根据ASCII码进行比较
memchr
void *memchr(const void *str, int c, size_t n)
功能:查找str指向的内存空间中前n个字节,是否出现字符c
返回值:如果查找成功,返回str中出现字符c的地址。如果查找失败,返回NULL
注意:
- 传入的字符是以int的形式传入的,查找是都是将这个值看作无符号字符
内存操作误区
以下的操作是错误的,需要注意:
- 对NULL指针进行解引用
int* a = NULL;
*a = 6; // 对NULL进行解引用
- 对动态开辟的内存空间进行越界的访问
int i = 0;
int* num = NULL;
num = (int*)malloc(10*sizeof(int));
for(i=0; i<=10; i++) // 循环到第11次时,*num访问的空间越界
{
*num = i;
num++;
}
- 对非动态开辟的内存空间使用free
int a = 10;
int *pa = &a;
free(pa); // 使用free释放非动态开辟空间
- 对动态开辟的内存空间的一部分使用ree
int* pa = (int*)malloc(4*sizeof(int));
pa++;
free(pa); // 使用free释放动态开辟的部分空间
- 堆动态开辟的内存空间多次使用free
int *pa = (int*)malloc(sizeof(int));
free(pa);
free(pa); // 多次使用free释放动态开辟的空间
- 动态开辟内存空间后不使用free,有空间泄漏的隐患
int* p = NULL;
p = (int*)malloc(sizeof(int));
C/C++程序的内存分配
程序存储时的存储分配
程序在硬盘中存储时分为三部分:
- 代码区:用于存放CPU可执行的机器指令,是只读区域,不能被修改
- 静态数据区:用于存放被初始化的全局变量,静态变量(全局静态变量和局部静态变量),常量数据(如常量字符串)
- BSS代码区:用于存放未初始化的全局变量。BSS区域的数据在程序执行之前会被初始化为0或者NULL
程序运行时的存储分配
程序在载入内存运行时分为四个部分:
- 代码区:存放函数体的二进制码。对于顺序指令,只会执行一次。如有反复指令,则需要使用跳转,如有递归指令,则需使用栈
- 数据段:
1 . 初始化的数据段
2. 未初始化的数据段(BSS)
3. 只读数据段(常量数据)
4. 已经初始化的可读写数据段 - 栈:栈是由高地址向低地址生长的数据结构,是一块连续的内存,容量较小,用于存放函数的参数和局部变量。栈的空间由系统管理,无需程序员参与。
- 堆:堆是由低地址向高地址生长的数据结构,是一块不连续的内存,容量较大,用于开辟动态分配的内存,比如malloc,calloc,realloc。需要程序员自行申请和释放
柔性数组
柔性数组的概念:零长度的数组,通常定义于可变长度结构体中,从而使得结构体的大小可以动态的改变
柔性数组的定义方法:
struct unit
{
int a;
char arr[0];
}
柔性数组的特点
- 柔性数组不能是可变结构体中的唯一成员
- 柔性数组必须定义在结构体的末尾
- 可变结构体的大小,不包括柔性数组的大小
可变结构体中的柔性数组数组名本身只是个偏移量,不占用任何空间,仅仅代表了一个地址常量
柔性数组的使用
可以用作可变长度数据缓冲区,节约空间且使用方便
// *****************
// 数据缓冲区的声明 *
// *****************
// 可变长结构体数据缓冲区
// 长度可变,节约空间
// 只需free一次即可释放空间
struct zero_buffer
{
int len;
char data[0];
};
// 定长结构体数据缓冲区
// 长度固定,容易造成空间浪费
// 只需free一次即可释放空间
struct max_buffer
{
int len;
char data[MAX_LENGTH];
};
// 指针结构体数据缓冲区
// 长度固定,容易造成空间浪费
// 只需free两次即可释放空间
struct point_buffer
{
int len;
char* data;
};
// *****************
// 数据缓冲区的定义 *
// *****************
// 可变长结构体数据缓冲区
struct zero_buffer* zbuffer = NULL;
// 定长结构体数据缓冲区
struct max_buffer* mbuffer = NULL;
// 指针结构体数据缓冲区
struct point_buffer* pbuffer = NULL;
// *****************
// 数据缓冲区的使用 *
// *****************
// 可变长结构体数据缓冲区
zbuffer = (struct zero_buffer*)malloc(sizeof(struct zero_buffer) + len*sizeof(char));
// 定长结构体数据缓冲区
mbuffer = (struct zero_buffer*)malloc(sizeof(struct max_buffer) + MAX_LENGTH*sizeof(char));
// 指针结构体数据缓冲区
pbuffer = (struct zero_buffer*)malloc(sizeof(struct point_buffer) + sizeof(char*));
pbuffer->data = (char*)malloc(len); // 辅助pbuffer开辟data的空间
// *****************
// 数据缓冲区的销毁 *
// *****************
// 可变长结构体数据缓冲区
free (zbuffer);
zbuffer = NULL;
// 定长结构体数据缓冲区
free (mbuffer);
mbuffer = NULL;
// 指针结构体数据缓冲区
free (pbuffer);
free (pbuffer->pdata);
pbuffer = NULL;
pbuffer->pdata = NULL;
柔性数组的优势
- 无需提前定义数组大小
- 使用灵活,可动态开辟空间
- 对空间的使用效率更高
基本知识框架Xmind文件下载
链接: 资源下载