动态内存分配
堆
堆内存空间的特点:
手动申请,手动释放。哪怕是程序结束了都不会释放,只有在计算机重启的时候才会被释放
如何申请堆空间内存:系统提供了2个用来申请堆空间内存的函数
① 函数名:malloc
函数参数:int size
函数返回值:void* p
函数功能:向系统申请 size个字节的堆空间内存,然后内部产生一个通用指针p指向了该内存空间的首地址,最后返回p
通常情况下,size如何决定?根据申请的指针类型有关,例如申请了int类型的堆空间,size就是4。
我们可以使用 valgrind+./a.out
来查看程序运行时候的详细的内存管理
② 函数名:calloc
函数参数:int num,int size
函数返回值:void p
函数功能:向系统申请 num个模块的堆内存空间,每个模块size个字节,然后内部产生一个通用指针void* 指向该段堆内存的首地址,最后返回p
注意:calloc函数自动初始化申请的内存空间
注意:通常情况下,人为规定,申请单个模块使用malloc,申请多个模块形成数组,使用calloc
③ 函数名:realloc
函数参数:void* ptr,int size
函数返回值:void* ptr(这个函数必须放在等号的右侧)
函数功能:将ptr指向的堆空间,重新分配size个大小的堆空间
注意:reallc频繁的调用,会产生大量的内存碎片。因为realloc并不是直接调用malloc以及free实现,他是直接真的内存进行的操作。
所以,尽量不要使用realloc,而是自己别写一个堆空间的扩容函数
如何申请一个堆空间数组
一直强调:应该将数组看成连续的内存空间
所以堆空间的数组,只要申请连续的内存空间即可
系统提供了一个专门初始化内存的函数
函数名:memset
函数参数:void* ptr,int value,int size
返回值:void* ptr
函数功能:将ptr指向的地址视作首地址,总共size个地址上的值全都设置成value
栈空间和堆空间的区别
#include<stdio.h>
#include<stdlib.h>
int* func(){
int* p = malloc(sizeof(int));
/*
注意:这里p依旧是一个栈空间指针变量,只不过该指针指向了堆空间
所以:p的地址属于栈空间地址,p的值属于堆空间地址
所以第5行代码产生了2段内存空间,等号左边自动产生了8个字节栈空间内存,等号右边手动申请了4个字节的堆空间内存
*/
*p = 5;//堆空间的第一个元素被赋值5
return p;//返回的是堆空间的首地址
//程序结束,栈空间被释放,而堆空间还在
}
int main(){
//申请一个int类型的指针,指向一个堆空间
int* pa = func();
printf("%d\n",*pa);
free(pa);
return 0;
}
堆空间的赋值
#include<stdio.h>
#include<stdlib.h>
int main(){
int* arr = malloc(sizeof(int)*5);//因为函数malloc返回的是一个通用指针,所以要声明定义一个指针来接收
arr[0] = 1;
arr[1] = 2;
*(arr+2) = 3;
*(arr+3) = 4;
arr[4] = 5;
int i = 0;
for(i=0;i<5;i++){
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
堆空间的初始化
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
//int* arr = malloc(sizeof(int)*5);
/*
malloc申请的内存空间,都没有进行过初始化,此时如果对未初始化的指针进行访问,则会有内存错误
*/
/*
系统提供了一个专门初始化内存的函数
函数名:memset
函数参数:void* ptr,int value,int size
返回值:void* ptr
函数功能:将ptr指向的地址视作首地址,总共size个地址上的值全都设置成value
*/
//memset(arr,0,sizeof(int)*5);
int* arr = calloc(5,sizeof(int));
printf("%d\n",*arr);
printf("%d\n",arr[1]);
printf("%d\n",arr[2]);
free(arr);
return 0;
}
扩容
例如:有一个结构体数组,容量为5,当他添加了5个结构体后,数组就被填满,此时,如果需要再次添加数据,则需要对该数组进行扩容。
但是数组本身的容量无法扩大,所以所谓的扩容就是指申请一个更大容量的数组,然后将原来数组中所有的内容拷贝给新的数组,最后再将指向原来数组的指针全都指向新的数组
注意,无论如何扩容,如果更大的数组是在栈空间上申请的,一旦函数结束,那么该栈空间就会被释放,这个指向数组的指针就成为野指针。
原因就在于:栈空间都是自动申请自动释放。
所以,动态内存分配必须使用到堆内存空间
动态扩容堆空间
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/*
函数名:expend
函数参数:int* arr-堆空间的首地址,int vol-数组长度
函数返回值:int* arr-堆空间首地址
函数功能:新开辟一个大小为旧堆空间两倍的堆空间,并且把旧堆空间数据复制到新堆空间释放旧堆空间。返回新堆空间的首地址
*/
int* expend(int* arr,int vol){
int* newArr = malloc(vol*4);//newArr指针指向新的堆空间
memset(newArr,0,sizeof(int)*vol);//newArr指向的堆空间总共sizeof(int)*vol个字节都置0
memcpy(newArr,arr,sizeof(int)*vol/2);//因为数组长度vol已经扩大为2倍,除以2才能正确且省时的将旧的堆空间数组复制到新的堆空间空间
free(arr);//释放旧的堆空间数组,注意:释放的是arr指向的堆空间数组,指针arr并没有消失
arr = newArr;//把指针arr指向新的堆空间
return arr;
}
/*
函数名:insert
函数参数:int** p-指向堆空间的指针的地址,int* len,int* vol-数组长度
函数返回值:void
函数功能:向堆空间添加数据
*/
void insert(int** p,int* len,int* vol){
int i = *len+1;
if(*len >= *vol){
printf("数组扩容中\n");
*vol *= 2;//数组长度翻倍
*p = expend(*p,*vol);
}
(*p)[(*len)] = i;
(*len)++;
}
int main(){
int* arr = malloc(20);//用malloc定义一维数组。注意:指向malloc的指针arr不要随便移动
//int _arr[20];//数组名arr相当于 int* const arr,指针常量,arr的指向被固定。
int** p = &arr;//指向一维数组的指针
//int* _p = _arr;//指向一维数组的指针
int len = 0;//数组下标
int vol = 5;//数组长度
int i = 0;
while(len<20){
insert(p,&len,&vol);
for(i=0;i<len;i++){
printf("%d ",arr[i]);//因为arr为int类型的,偏移一下就移动4个字节,正好可以正确的读取整形数据
}
printf("\n");
}
return 0;
}
申请堆空间实现冒泡排序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int IntCmp(const void *pv1,const void *pv2);
int FloatCmp(const void *pv1,const void *pv2);
int BubbleSort(void *pbase,int nmemb,int size,int (*pfCmp)(const void *,const void *));
int main()
{
int arr1[5] = {13,19,16,27,8};
float arr2[5] = {1.3,1.9,1.6,2.7,8.0};
int i = 0;
//qsort(arr1,5,sizeof(int),IntCmp);
BubbleSort(arr1,5,sizeof(int),IntCmp);
for(i = 0;i < 5;i++)
{
printf("%d ",arr1[i]);
}
printf("\n");
//qsort(arr2,5,sizeof(float),FloatCmp);
BubbleSort(arr2,5,sizeof(float),FloatCmp);
for(i = 0;i < 5;i++)
{
printf("%.1f ",arr2[i]);
}
printf("\n");
return 0;
}
int FloatCmp(const void *pv1,const void *pv2)
{
float f1 = *(float *)pv1;
float f2 = *(float *)pv2;
if(f1 == f2)
{
return 0;
}
else if(f1 > f2)
{
return 1;
}
else
{
return -1;
}
}
int IntCmp(const void *pv1,const void *pv2)
{
return *(int *)pv1 - *(int *)pv2;
}
int BubbleSort(void *pbase,int nmemb,int size,int (*pfCmp)(const void *,const void *))
{
void * pt = NULL;
char *pc = (char *)pbase;
int i = 0;
int j = 0;
if(NULL == pbase || nmemb <= 0 || size <= 0 || NULL == pfCmp)
{
printf("Input param is invalid\n");
return -1;
}
pt = malloc(size);
if(NULL == pt)
{
perror("malloc failed");
return -1;
}
memset(pt,0,size);
for(i = 0;i < nmemb;i++)
{
for(j = 0;j < nmemb - i - 1;j++)
{
if(pfCmp(pc+j*size,pc+(j+1)*size) > 0)
{//交换
memcpy(pt,pc+j*size,size);
memcpy(pc+j*size,pc+(j+1)*size,size);
memcpy(pc+(j+1)*size,pt,size);
}
}
}
free(pt);
pt = NULL;
return 0;
}