8-动态内存申请

147 阅读5分钟

知识点一:申请和释放空间

malloc函数

用于申请一块连续的指定大小的内存空间(堆区),并返回一个指向它的指针

头文件:#include <stdlib.h>

void *malloc(unsigned int num_ size);
//形参: num_ size需要申请空间大小的字节数
    
返回值:
	成功:返回空间的起始地址
	失败: NULL    
  • 对于malloc的返回值一般要强制类型转换再赋值保存(返回值就是开辟对应的空间)
  • malloc申请的空间内容不确定,一般使用memset进行清空
  • 多次调用malloc第1次malloc 和 第2次malloc的地址不一定连续

free函数

void free(void *addr);        ()里面是保存malloc保存返回值的指针变量名
//功能:释放堆区空间

从堆区申请 一个 int 空间

int get_n(void)
{
    int n = 0;
    printf("请输入元素的个数:");
    scanf("%d", &n);
    return n;
}
int* get_addr(int n)
{
	return (int *)malloc(n*sizeof(int));
}
void my_input_array(int *arr, int n)
{
    int i=0;
    //记得将arr指向的空间清0
    memset(arr, 0, n*sizeof(int));

    //获取键盘输入
    printf("请输入%d个int数据\n",n);

    for(i=0;i<n; i++)
    {
    	scanf("%d", arr+i);
    }
}
void my_print_array(int *arr, int n)
{
    int i=0;
    for(i=0;i<n;i++)
    {
    	printf("%d ", arr[i]);
    }
    printf("\n");
}
int main(int argc,char *argv[])
{
    int *arr=NULL;
    int n = 0;

    //得到用户输入的元素个数
    //1、获取用户大小
    n = get_n();

    //定义函数 给arr申请堆区空间
    arr = get_addr(n);
    if(arr == NULL)
    {
        perror("get_addr");
        return;
    }

    //对空间读写操作
    my_input_array(arr, n);

    //对空间数组遍历
    my_print_array(arr, n);

    //释放空间
    free(arr);
    return 0;
}

知识点二:内存块清零

将指定的值 c 复制到 str 所指向的内存区域的前 n 个字节中,这可以用于将内存块清零或设置为特定值

#include <string.h>
void *memset(void *str, int c, size_t n)

形参:
    str:指向要填充的内存区域的指针
    c:要设置的值,通常是一个无符号字符
    n:要被设置为该值的字节数
    
返回值:返回一个指向存储区 str 的指针
#include <stdio.h>
#include <string.h>
 
int main ()
{
    char str[50];

    strcpy(str, "This is string.h library function");
    puts(str);	//This is string.h library function

    //将str前7个字符,替换成$''
    memset(str, '$', 7);	//$$$$$$$ string.h library function
    puts(str);

    return(0);
}

知识点二:calloc函数

申请的空间自动清零,不需要memset清零

头文件:#include<stdlib.h>
// (强转类型 *)calloc(n,sizeof(类型))  n:无符号整数,()里面的字节数是n乘类型的字节数
void * calloc(size_t nmemb,size_ t size);
参数:
    1、nmemb:申请的数据块数
    2、size:每一块大小
申请总大小 = nmemb*size
返回值:
    成功:返回空问的起始地址
    失败:返回NULL
void test04()
{
    int n = 0;
    int i = 0;
    int *arr = NULL;

    //1、获取用户大小
    printf("请输入元素的个数:");
    scanf("%d", &n);

    //2、根据大小从堆区申请空间
    #if 0
    arr = (int *)malloc(n * sizeof(int));
    if(NULL == arr)
    {
        //perror 错误输出
        perror("mallac");
        return;
    }
    memset(arr,0,n*sizeof(int));	//清零
    #endif

    #if 1
    arr=(int *)calloc(n, sizeof(int));	//自动清零 不需要使用memset
    if(NULL == arr)
    {
        //perror 错误输出
        perror("calloc");
        return;
    }
    #endif
    
    //对arr的读写操作
    printf("请输入%d个int数据\n",n);
    for(i = 0; i < n; i++)
    {
    	scanf("%d", arr+i);
    }

    //遍历数组
    for(i = 0; i < n; i++)
    {
    	printf("%d ", arr[i]);
    }

    //释放空间
    free(arr);
}

知识点三:realloc动态追加或减少空间

在原先s指向的内存基础.上重新申请内存,新的内存的大小为new_ size 个字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不够用,则relloc函数会在堆区找一个newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址

#include<stdlib.h>
//(强转类型 *)realloc(保存原先开辟内存的指针名, (原先空间+新增空间)*sizeof(类型))
void* realloc(void *S,unsigned int newsize);

参数: 
    s:原先开辟内存的首地址
    newsize: 新申请的空间的总大小(原先+新增部分大小)(如果新申请的空间比原先大,则是追加,如果比原先小,则是减少)
        
返回值:新申请的内存的首地址
realloc的返回值一般要强制类型转换再赋值保存(返回值就是开辟对应的空间)
int main(int argc,char *argv[])
{
    int *arr = NULL;
    int n = 0;
    int i=0;
    int n_new = 0;

    //1、获取用户大小
    printf("请输入元素的个数:");
    scanf("%d", &n);

    arr = (int *)calloc(n, sizeof(int));	//自动清零 不需要使用memset
    if(NULL == arr)
    {
        //perror 错误输出
        perror("calloc");
        return;
    }

    printf("请输入%d个int数据\n",n);
    for(i = 0;i < n; i++)
    {
    	scanf("%d", arr+i);
    }

    //遍历数组
    for(i = 0; i < n; i++)
    {
    	printf("%d ", arr[i]);
    }

    //再追加5个元素
    printf("请输入新增的元素个数:");
    scanf("%d", &n_new);
    arr = (int *)realloc(arr, (n+n_new)*sizeof(int));

    printf("请输入新增的%d个int数据\n",n_new);
    for(i = n; i < (n+n_new); i++)
    {
    	scanf("%d",arr+i);
    }

    for(i = 0; i < (n+n_new); i++)
    {
    	printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    return 0;
}

知识点四:堆区空间使用注意事项

void test1()
{
    int *p2 = NULL;
    int *p3 = NULL;
    //1、指向堆区空间的指针变量不要随意的更改指向
    int *p = (int *)calloc(1, sizeof(int)) ;
    int num = 10;
    p = &num;    //p指向num导致calloc申请的空间泄露
    
    //2、不要操作已经释放的空间
    p2 = (int *)calloc(1,sizeof(int));
    *p2 = 1000;
    //释放该空间
    free(p2) ;
    printf("*p2 = %d\n, *p2);	//不确定
           
    //3、不要对堆区空间重复释放
    p3 = (int *)calloc(1, sizeof(int));
    free(p3);
    free(p3);	//多次释放
}

知识点五:防止多次释放

void test09 ()
{
    int *p = (int *)calloc(1,sizeof(int));
    if(p != NULL)    //防止多次释放
    {
        free(p);
        p=NULL;
    }
    if(p != NULL)    
    {
        free(p);
        p=NULL;
    }
}