指针和动态储存->malloc calloc realloc free
内容为自学笔记,如有错误还望指正
在了解这几个函数之前,先了解一下void类型指针:
它是一种通用指针类型,它不针对某个特定的数据类型
为什么说它是通用指针类型?如图,当char类型指针p2指向int类型指针p1编译器会报错,但void类型指针p3指向int型指针p1并没有发生报错
p3=p1并不需要指针类型的转换,p3=p1是合法的,并不会有编译错误。
int main() {
int A[5] = { 1,2,3,4,5 };
int* p1 = &A[0];
void* p2 = &A[0];
printf("指针p1所指地址:%d\n", p1);
printf("指针p2所指地址:%d\n", p2);
}
运行结果如下:
int main() {
int A[5] = { 1,2,3,4,5 };
int* p1 = &A[0];
void* p2 = &A[0];
printf("指针p1所指地址:%d,所指地址存储值:%d\n", p1,*p1);
printf("指针p2所指地址:%d,所指地址存储值:%d\n", p2,*p2);//*p2会报错
}
printf(“指针p2所指地址:%d,所指地址存储值:%d\n”, p2,*p2 );
但是当我们解引用指针p1,p2指向地址所存储的值,会发现,代码中*p2会报错,提示表达式必须是指向完整对象类型的指针
这是因为void这种类型指针没有映射到任何特定类型,如int类型,不能直接对它解引用。
—printf(“address of A[1]:%d”,p2+1 )
同时也不能进行指针算数运算
堆上分配内存的函数
在之前已经了解到,void* 是一个通用指针,所以malloc、calloc、realloc函数都是通用函数,只在堆上分配内存,并不在乎存储的内容是什么数据类型(int,char…),它只是简单的返回指向这个内存块首地址的void指针。
所以为了能过使用这三个函数,必须首进行指针类型转换。
返回首地址201
malloc函数
函数定义:
void* malloc(size_t size)
参数是内存块的字节数大小,比如int型是4字节内存块
我们注意到参数的数据类型是size_t,我们可以把它想象成类型unsigned int,只能是0和正整数,不能是负数
(size_t这个特定类型的目的就是保证参数只能是0或正整数)
使用:
int* A = (int*)malloc(sizeof(int));
int* B = (int*)malloc(4 * sizeof(int));
将void* malloc数据类型转换为int*型
创建一个int型大小的内存块并将首地址返回个int型指针A
创建四个int型大小的内存块并将首地址返回个int型指针B
int main() {
int* A = (int*)malloc(sizeof(int));
int* B = (int*)malloc(4 * sizeof(int));
*A = 1;
*B = 1;
*(B + 1) = 2;
* (B + 2) = 3;
printf("value of A:%d\n", *A);
printf("value of arrB:%d ,%d ,%d", *B, *(B + 1), *(B + 2));
printf("\n\n\n\n\n\n");
}
*A = 1;
*B = 1;
*(B + 1) = 2;
* (B + 2) = 3;
等价于
A[0] = 1;
B[0] = 1;
B[1] = 2;
B[2] = 3;
这里要注意的是,如果没有为开创的内存块赋值,则会产生不确定的值。
calloc函数
与malloc函数类似。
函数定义:
void* calloc(size_t num,size_t size)
size_t num 参数是想要创建某一数据类型的个数
size_t size 参数是内存块的字节数大小,比如int型是4字节内存块
int* A = (int*)malloc(5 * sizeof(int));
int* B = (int*)calloc(5, sizeof(int));
这两句实现的内容是一样的,都是创建5个int型大小的内存块
其中要注意的是calloc创建的内存块会自动赋初始值。
int main() {
int* A = (int*)malloc(5 * sizeof(int));
int* B = (int*)calloc(5, sizeof(int));
for (int i = 0; i < 5; i++)
printf("%d\n",A[i]);
for (int i = 0; i < 5; i++)
printf("%d\n", B[i]);
}
运行结果:
realloc函数
函数定义:
void* realloc(void* pointer,size_t size)
第一个参数是指向已分配内存的起始地址的指针
第二个参数是新的内存块的大小
使用场景:
如果有一块内存存放n个元素,我们想让它能够存储的元素数增加或减少
(比如一开始分配了能够存放5个元素的内存块,如果我们想要它能够存储10个元素,或只能存储2个元素可以使用realloc,内存块大小会发生改变。)
工作方式:
请求的新内存块大于之前的块,如果可以扩展之前的块(能够在之前的块的基础上找到连续的内存),那么拓展之前的块,否则就分配新的内存,被之前的块中的内容拷贝过去,然后释放之前内存块。
int main() {
int* A = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++)
A[i] = i + 1;
A = (int*)realloc(A, 10 * sizeof(int));//从可存放5个元素内存扩展到可存放10个元素内存
for (int i = 0; i < 10; i++)
printf("%d ", A[i]);
printf("\n");
A = (int*)realloc(A, 3 * sizeof(int));//再从可存放10个元素内存减少到可存放3个元素内存
for (int i = 0; i < 3; i++)
printf("%d ", A[i]);
}
运行结果:
同时realloc有时也可以用free和malloc替代:
int main() {
int* A = (int*)malloc(5 * sizeof(int));
A = (int*)realloc(A, 0);//等同于free(A)
int* B = (int*)realloc(NULL, 5 * sizeof(int));//等同于int* B=malloc(5*sizeof(int))
}
堆上释放内存的函数
free
任何分配了的动态内存在程序结束之前会一直存在(在堆中一直占据着内存,不会像栈一样自动释放内存) ,除非自己来释放这部分内存。
malloc、calloc、realloc分配的内存,要使用free函数来释放
free的参数是内存块的起始地址
int main() {
int* A = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++)
{
A[i] = i + 1;
printf("%d ", A[i]);
}
printf("\n");
free(A);
for (int i = 0; i < 5; i++)
printf("%d ", A[i]);
}
运行结果: