《C和指针》阅读笔记(10)---动态内存

本文继续对《C和指针》的第11章进行解读,这一章的内容非常简单,主要介绍了4个和动态内存分配相关的系统API,怎样用好这几个API进行动态内存分配及管理才是我们需要掌握的。废话少说,干就完了!

malloc

void *malloc(size_t size);

malloc在heap上动态分配size大小的内存,如果分配成功,则返回一个指向这块内存的指针;如果失败,则返回NULL。

作为一个c语言的程序员,一个好的编码习惯是在malloc后,立即编写free的代码,以免后面忘记,最终是程序出现内存泄漏的问题。

free

void free(void *ptr);

即释放掉前面malloc(或者其他动态内存分配函数)分配的内存,ptr表示被释放内存的地址。

按照惯例,先来一个示例程序来展示这个两个接口的简单使用。demo1.c演示了一个16字节长度的动态内存的申请与释放。

#include <stdio.h>
#include <stdlib.h>

#define BLK_SIZE (16)

int main(int argc, char** argv)
{
    void *p = malloc(BLK_SIZE); // 申请16字节的内存
    if (NULL == p) // 判断是否申请成功
    {
        // 申请失败提示
        printf("allocate dynamic memory failed\n");
        return -1;
    }
    // 申请成功,则打印成功提示
    printf("allocate dynamic memory successfully, size is %d\n", BLK_SIZE);

    // 在实际的工程代码中,通常在释放前会对指针进行判空
    if (NULL != p)
    {
        free(p);
        p = NULL;
        printf("free dynamic memory successfully\n");
    }
    return 0;
}

我们知道,数组的大小是在编译时就确定了,而动态内存的大小是在运行时确定的。下面这个示例就说明了这一点。

#include <stdio.h>
#include <stdlib.h>

#define BLK_SIZE (16)

int main(int argc, char** argv)
{
    int i = 0;
    // 由程序运行时通过命令行参数指定动态内存的大小
    int dy_size = atoi(argv[1]);

    //const int st_size = 16;
    //char arr[st_size] = {0};
    //需要注意,在定义数组时,在给定数组的大小时不能使用变量,否则编译器会报编译错误信息
    //error: variable-sized object may not be initialized
    char arr[BLK_SIZE] = {0};

    // 申请动态内存
    char *p = (char*)malloc(dy_size);
    if (NULL == p)
    {
        printf("allocate dynamic memory failed\n"); 
        return -1;
    }
    printf("allocate dynamic memory successfully, size is %d\n", dy_size);

    // 给数组的每个元素赋值
    for (i = 0; i < BLK_SIZE; ++i)
        arr[i] = i;

    // 给动态内存的每个元素赋值
    for (i = 0; i < dy_size; ++i)
        *(p+i) = i;

    // 打印数组的所有元素
    printf("print all elements of array:\n");
    for (i = 0; i < BLK_SIZE; ++i)
        printf("arr[%d] = %d\n", i, arr[i]);
    
    // 打印动态内存的所有元素
    printf("print all elements of dynamic memory:\n");
    for (i = 0; i < dy_size; ++i)
        printf("mem[%d] = %d\n", i, p[i]);

    if (NULL != p)
    {
        free(p);
        p = NULL;
        printf("free dynamic memory successfully\n");
    }
    return 0;
}

我们来看看两次运行的结果:(第一次命令行参数指定动态内存的大小为8,第二次命令行参数指定动态内存的大小为16)

$ ./demo2 8
allocate dynamic memory successfully, size is 8
print all elements of array:
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4
arr[5] = 5
arr[6] = 6
arr[7] = 7
arr[8] = 8
arr[9] = 9
arr[10] = 10
arr[11] = 11
arr[12] = 12
arr[13] = 13
arr[14] = 14
arr[15] = 15
print all elements of dynamic memory:
mem[0] = 0
mem[1] = 1
mem[2] = 2
mem[3] = 3
mem[4] = 4
mem[5] = 5
mem[6] = 6
mem[7] = 7
free dynamic memory successfully
$ ./demo2 16
allocate dynamic memory successfully, size is 16
print all elements of array:
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4
arr[5] = 5
arr[6] = 6
arr[7] = 7
arr[8] = 8
arr[9] = 9
arr[10] = 10
arr[11] = 11
arr[12] = 12
arr[13] = 13
arr[14] = 14
arr[15] = 15
print all elements of dynamic memory:
mem[0] = 0
mem[1] = 1
mem[2] = 2
mem[3] = 3
mem[4] = 4
mem[5] = 5
mem[6] = 6
mem[7] = 7
mem[8] = 8
mem[9] = 9
mem[10] = 10
mem[11] = 11
mem[12] = 12
mem[13] = 13
mem[14] = 14
mem[15] = 15
free dynamic memory successfully

calloc

void *calloc(size_t nmemb, size_t size);

calloc和malloc的作用类似,也是分配动态内存,但有两点不同

  • calloc的参数个数有两个,需要指定元素个数以及每个元素的大小;
  • calloc会把分配的内存初始化为0,而malloc分配的内存是随机值;

demo3.c 演示了calloc的使用,以及calloc的会初始化内存为0的特性,同时也展示了malloc分配的内存是随机值。

#include <stdio.h>
#include <stdlib.h>

#define BLK1_SIZE (160)
#define BLK_SIZE (16)

void myfree(void *p)
{
    if (NULL != p)
    {
        free(p);
        p = NULL;
        printf("free dynamic memory successfully\n");
    }
}

int main(int argc, char** argv)
{
    int n       = 0;
    int i       = 0;
    int *p1    = NULL;
    int *p11   = NULL;
    int *p2    = NULL;

    p1 = (int*)malloc(BLK1_SIZE*sizeof(int));
    if (NULL == p1)
    {
        printf("malloc failed\n"); 
        return -1;
    }
    printf("\np1 malloc(%p) successfully, size is %ld\n", p1, BLK1_SIZE*sizeof(int));

    // 给这个大块内存赋值
    for (i = 0; i < BLK1_SIZE; ++i)
       p1[i] = i; 

    // 打印动态内存p1的所有元素
    printf("print all elements of p1 memory(%p):\n", p1);
    for (i = 0; i < BLK1_SIZE; ++i)
    {
        printf("%03d ", p1[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    myfree(p1);
    printf("\n\n");

    n = 0;
    p11 = (int*)malloc(BLK_SIZE*sizeof(int));
    if (NULL == p11)
    {
        printf("malloc failed\n"); 
        return -1;
    }
    printf("p11 malloc(%p) successfully, size is %ld\n", p11, BLK_SIZE*sizeof(int));

    printf("print all elements of p11 memory(%p):\n", p11);
    for (i = 0; i < BLK_SIZE; ++i)
    {
        printf("%03d ", p11[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    printf("\n");
    myfree(p11);


    n = 0;
    p2  = (int*)calloc(BLK_SIZE, sizeof(int));// 16个元素,每个元素占4个字节
    if (NULL == p1)
    {
        printf("calloc failed\n"); 
        return -1;
    }
    printf("calloc(%p) successfully, size is %ld\n", p2, BLK_SIZE*sizeof(int));

    printf("print all elements of calloc memory:\n");
    for (i = 0; i < BLK_SIZE; ++i)
    {
        printf("%03d ", p2[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    printf("\n");

    myfree(p2);


    return 0;
}

在这里插入图片描述

绿色区域是第一次通过malloc申请了160个int大小的内存,并将这160个元素赋值(从0到159),然后将其释放。

蓝色区域是再次通过malloc申请了16个int大小的内存,然后直接打印这块内存的值,发现这块内存的值是随机的,并没有被初始化。另外,p11的地址为p1上次申请的内存地址,也说明,p1释放后,p11申请内存时会复用之前的内存空间。

红色区域是通过calloc申请了16个int大小的内存,然后直接打印这块内存的值,发现这块内存的值都被初始化为0。

说明,打印中的内存的地址每次执行都不同。

这个例子说明了,对于malloc申请的内存,一种好的习惯是将这块内存进行初始化(使用memset)。

realloc

void *realloc(void *ptr, size_t size);

realloc可以扩大或缩小一块内存;若用于扩大,则原先内存的内容不变,新增的内存追加到原内存后,新内存是没有初始化的;若用于缩小,原内存尾部的冗余空间将被释放。如果原内存块的大小不能被改变,那么将会重新分配一块正确大小内存,并把原内存的内容拷贝到新的内存块上来,这种情况下就需要使用realloc返回的指针,原先的指针就不能再使用了,原内存会被自动释放。

如果ptr为NULL,realloc等价于malloc。

#include <stdio.h>
#include <stdlib.h>

#define BLK_SIZE (16)

int main(int argc, char** argv)
{
    int i = 0;
    int n = 0;
    char *p1 = NULL;
    char *p2 = NULL;
    size_t cap = 0;

    p1 = (char*)malloc(BLK_SIZE);
    if (NULL == p1)
    {
        printf("allocate dynamic memory failed\n"); 
        return -1;
    }
    printf("p1(%p) malloc successfully, size is %d\n", p1, BLK_SIZE);
    for (i = 0; i < BLK_SIZE; ++i)
       p1[i] = i; 

    // 打印动态内存p1的所有元素
    printf("print all elements of p1 memory(%p):\n", p1);
    for (i = 0; i < BLK_SIZE; ++i)
    {
        printf("%03d ", p1[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    printf("\n");


    // 扩容
    cap  = BLK_SIZE<<1;
    p2 = realloc((void*)p1, cap);
    if (NULL == p2)
    {
        printf("allocate dynamic memory failed\n"); 
        return -1;
    }
    printf("p2(%p) malloc successfully, size is %lu\n", p2, cap);

    n = 0;
    printf("print all elements of p1 memory(%p):\n", p1);
    for (size_t i = 0; i < cap; ++i)
    {
        printf("%03d ", p2[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }

    free(p2);
    return 0;
}

在这里插入图片描述

从demo4的打印信息可以看出,扩容后,原内存中的数据不变。

最后,再介绍一个动态内存压缩的示例

#include <stdio.h>
#include <stdlib.h>

#define BLK_SIZE (16)

int main(int argc, char** argv)
{
    int i = 0;
    int n = 0;
    char *p1 = NULL;
    char *p2 = NULL;
    size_t cap = 0;

    p1 = (char*)malloc(BLK_SIZE);
    if (NULL == p1)
    {
        printf("allocate dynamic memory failed\n"); 
        return -1;
    }
    printf("p1(%p) malloc successfully, size is %d\n", p1, BLK_SIZE);
    for (i = 0; i < BLK_SIZE; ++i)
       p1[i] = i; 

    // 打印动态内存p1的所有元素
    printf("print all elements of p1 memory(%p):\n", p1);
    for (i = 0; i < BLK_SIZE; ++i)
    {
        printf("%03d ", p1[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    printf("\n");


    // 缩小
    cap  = BLK_SIZE>>1;
    p2 = realloc((void*)p1, cap);
    if (NULL == p2)
    {
        printf("allocate dynamic memory failed\n"); 
        return -1;
    }
    printf("p2(%p) malloc successfully, size is %lu\n", p2, cap);

    n = 0;
    printf("print all elements of p1 memory(%p):\n", p1);
    for (size_t i = 0; i < cap; ++i)
    {
        printf("%03d ", p2[i]);
        n++;
        if (n % BLK_SIZE == 0)
            printf("\n");
    }
    printf("\n");

    free(p2);
    return 0;
}

在这里插入图片描述

好了,动态内存分配就介绍这么多。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页