动态内存分配
- 1.常用函数
- 2.malloc函数的细节点
- 3.其他三个函数的细节点
- 4.C语言的内存结构
- 5.变量数组在内存中的运行情况
- 6.全局变量和static变量在内存中的运行情况
- 7.字符串在内存中的运行情况
- 8.malloc函数在内存中的运行情况
1.常用函数
函数名 | 全部单词 | 作用 |
---|---|---|
malloc | memory allocation | 申请空间(连续) |
calloc | contiguous allocation | 申请空间+数据初始化 |
realloc | re-allocation | 修改空间大小 |
free | free | 释放空间 |
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*
malloc 申请空间(连续) 掌握
calloc 申请空间+数据初始化 了解
realloc 修改空间大小 了解
free 释放空间 掌握
stdlib.h
*/
// 1.利用malloc函数申请一片连续的空间
// 需求:申请一片空间,要存储10个int类型的整数
// 返回这片空间的首地址
int *p = malloc(10 * sizeof(int));
// int*p = calloc(10, sizeof(int));
printf("%p\n", p);
// // 2.赋值
int i;
for(i=0;i<10;i++)
{
// 第一种赋值
// *(p+i) = (i+1)*10;
// 第二种赋值
p[i] = (i+1)*10;
// p[i] ----> p+i
}
// 3.遍历
for(i=0;i<10;i++)
{
// printf("%d ", *(p+i));
printf("%d ", p[i]);
}
// 4.扩容,20个int类型的整数
int* pp = realloc(p, 20*sizeof(int));
for(i=0;i<20;i++)
{
// printf("%d ", *(p+i));
printf("%d ", p[i]);
}
// 5.释放空间
// 如果申请的空间不再使用了,那么记得一定要释放
free(pp);
return 0;
}
2.malloc函数的细节点
- malloc创建空间的单位是字节
- malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
- malloc返回的不仅仅是首地址,没有总大小,最好定义一个变量记录总大小
- malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄漏
- malloc申请的空间过多,会产生虚拟内存
- malloc申请的空间没有初始化值,需要先赋值才能使用
2.1 malloc创建空间的单位是字节
void* p = malloc(100); 创建的是100字节的空间
2.2 malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
int* p = malloc(25*sizeof(int)); // 因为void类型具有通用性
2.3 malloc返回的不仅仅是首地址,没有总大小,最好定义一个变量记录总大小
// int*:指针的步长
// p:首地址
int* p = malloc(25*sizeof(int));
int size = 25;
2.4 malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄漏
int* p = malloc(25*sizeof(int));
int size = 25;
method(p, 25);
free(p); // 记得要释放内存空间,防止内存泄漏
2.5 malloc申请的空间过多,会产生虚拟内存
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*
malloc申请的空间过多,会产生虚拟内存
虚拟内存:
虚拟:假的
当申请的空间过多,因为每一个内存空间不会在刚申请的时候立马使用
所以c语言不会立马就在内存中开辟空间,而是什么时候存储数据,才会真正的分配空间
目的:为了提高内存的使用效率
*/
// 表示单次申请空间的字节大小(1G)
int number = 1024*1024*1024;
// 利用循环不断的申请空间
// malloc 申请空间(连续)
// 如果申请空间成功,返回空间的首地址
// 如果申请空间失败,返回NULL
int count = 0;
while (1)
{
int* p = malloc(number);
if (p == NULL)
{
printf("申请失败");
break;
}
printf("内存%d申请成功:%p\n", count++, p);
}
return 0;
}
在linux系统中运行输出的结果如下:
…
内存119614申请成功:0x34cf806d53f0
内存119615申请成功:0x34cfc06d5400
内存119616申请成功:0x34d0006d5410
申请失败
2.6 malloc申请的空间没有初始化值,需要先赋值才能使用
int *p = malloc(10 * sizeof(int));
// int*p = calloc(10, sizeof(int));
printf("%p\n", p);
// 2.赋值
int i;
for(i=0;i<10;i++)
{
// 第一种赋值
// *(p+i) = (i+1)*10;
// 第二种赋值
p[i] = (i+1)*10;
// p[i] ----> p+i
}
3.其他三个函数的细节点
- free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
- calloc就是在malloc的基础上多了个初始化的动作
- realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失
- realloc修改之后,无需释放原来的空间,函数底层会进行处理
3.1 free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
#include <stdio.h>
#include <stdlib.h>
void method(int* p, int size);
int main()
{
/*
- free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
*/
int* p = malloc(10*sizeof(int));
int size = 10;
// 赋值
int i;
for(i=0;i<10;i++)
{
*(p+i) = (i+1)*10;
}
printf("遍历空间中的数据:\n");
method(p, 10);
free(p);
printf("再次遍历空间中的数据:\n");
method(p, 10);
return 0;
}
// 遍历
void method(int* p, int size)
{
int i;
for(i=0;i<size;i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
输出如下:
遍历空间中的数据:
10 20 30 40 50 60 70 80 90 100
再次遍历空间中的数据:
0 0 30 40 50 60 70 80 90 100
3.2 calloc就是在malloc的基础上多了个初始化的动作
此处知识点见第1节
3.3 realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失(如果内存中已经无法申请空间了,会返回NULL)
#include <stdio.h>
#include <stdlib.h>
void method(int* p, int size);
int main()
{
/*
- realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失
- realloc修改之后,无需释放原来的空间,函数底层会进行处理
*/
int* p = malloc(10*sizeof(int));
printf("最初的内存地址为:%p\n", p);
int size = 10;
// 赋值
int i;
for(i=0;i<10;i++)
{
*(p+i) = (i+1)*10;
}
printf("遍历空间中的数据:\n");
method(p, 10);
int* p2 = realloc(p, 15*sizeof(int));
printf("修改之后的内存地址为:%p\n", p2);
size = 20;
method(p2, size);
return 0;
}
// 遍历
void method(int* p, int size)
{
int i;
for(i=0;i<size;i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
输出如下:
最初的内存地址为:0x602010
遍历空间中的数据:
10 20 30 40 50 60 70 80 90 100
修改之后的内存地址为:0x602010
10 20 30 40 50 60 70 80 90 100 135121 0 0 0 0 0 0 0 135089 0
3.4 realloc修改之后,无需释放原来的空间,函数底层会进行处理
#include <stdio.h>
#include <stdlib.h>
void method(int* p, int size);
int main()
{
int* p = malloc(10*sizeof(int));
printf("最初的内存地址为:%p\n", p);
int size = 10;
// 赋值
int i;
for(i=0;i<10;i++)
{
*(p+i) = (i+1)*10;
}
printf("遍历空间中的数据:\n");
method(p, 10);
// realloc修改之后,无需释放原来的空间,函数底层会进行处理
// 如果内存地址不变,底层在原来空间的后面接着申请
// 如果内存地址变了,申请一个新的大的空间,把原来的数据拷贝到新的空间中,再把原来的空间给free掉
int* p2 = realloc(p, 15*sizeof(int));
printf("修改之后的内存地址为:%p\n", p2);
size = 20;
method(p2, size);
free(p2);
return 0;
}
// 遍历
void method(int* p, int size)
{
int i;
for(i=0;i<size;i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
4.C语言的内存结构
5.变量数组在内存中的运行情况
- 函数里面定义的变量和数组都是在栈里面的,所有变量数组的生命周期都是跟函数有关的。
- 函数进栈,变量数组存在,函数出栈,变量数组消失。
6.全局变量和static变量在内存中的运行情况
- 全局变量和static变量在静态区,根据变量有无赋值,划分在初始化静态区和未初始化静态区。
7.字符串在内存中的运行情况
- 只有通过指针+双引号定义的字符串,它的底层字符数组是存在常量区的(内容无法修改、数据会进行复用)
- 字符串如果赋值写成大括号数组形式,它的底层字符数组是存在栈里面的(内容可修改,数据不会复用)
8.malloc函数在内存中的运行情况
int* p = (int*)malloc(100);
*p = 10;
printf("%d\n", *p);
- (int*)malloc(100)是存在堆里面的
- 如果要完全的遍历堆里面的空间,最好有单独的变量去记录它的长度
- 我们普通定义的字符串都存在栈里面的,而malloc创建的空间是存在堆里面的;并且堆比栈大的多得多,存多少内容都可以