双宿双飞的 malloc 和 free

栏目: C · 发布时间: 4年前

内容简介:这两个函数是一对好朋友,几乎是形影不离。有今天跟大家聊聊在进行下面话题之前,我们先回忆一下

这两个函数是一对好朋友,几乎是形影不离。有 malloc 的地方就应该有 free 的存在。

今天跟大家聊聊 mallocfree 这对好基友,这两个函数都是对堆内存进行管理的函数,另外还有 callocreallocreallocfvalloc 等堆内存管理函数。

void *

在进行下面话题之前,我们先回忆一下 void * 是什么?

void * 表示未确定类型的指针。C/C++规定, void * 类型可以强制转换为任何其它类型的指针。

void * 也被称之为无类型指针, void * 可以指向任意类型的数据,就是说可以用任意类型的指针对 void * 赋值,如下示例:

void *p1;
int *p2;
p1 = p2;

但一般不会反过来使用,如下示例在有些编译器上面可以编译通过,有些就不行:

void *p1;
int *p2;
p2 = p1;

可以修改一下代码,将 void * 转换为对应的指针类型再进行赋值,如下示例:

void *p1;
int *p2;
p2 = (char *)p1;

由于 GNU 和 ANSI 对 void * 类型指针参与运算的规定不一样,所以为了兼容二者并且让程序有更好的兼容性,最好还是将 void * 转换为有明确类型的指针再参与运算,如下示例。

void *pd;
char *pc = (char *)pd;
pc ++;
pc += 1;

malloc

函数原型:

void * malloc(size_t size);

malloc 向系统申请分配指定 size 个字节的内存空间,即 malloc 函数用来从堆空间中申请指定的 size 个字节的内存大小,返回类型是 void * 类型,如果成功,就会返回指向申请分配的内存,否则返回空指针,所以 malloc 不保证一定成功。

查看函数手册或者直接在 linux 、macOS 上面直接 man malloc 会显示对应的函数信息:

The malloc() function allocates size bytes of memory and returns a pointer to the allocated memory.

If successful, malloc() function return a pointer to allocated memory.  
If there is an error, they return a NULL pointer and set errno to ENOMEM.

另外需要注意一个问题,使用 malloc 函数分配内存空间成功后, malloc 不会对数据进行初始化,里边数据是随机的垃圾数据,所以一般结合 memset 函数和 malloc 函数 一起使用。

int *arr;
arr = (int *)malloc(10 * sizeof(int));
if (NULL != arr) {
    memset(arr, 0, 10 * sizeof(int));
    printf("arr: %p\n", arr);
}
char *arr;
arr = (char *)malloc(10 * sizeof(char));
if (NULL != arr) {
    memset(arr, '\0', 10 * sizeof(char));
    printf("arr string: %s\n", arr);
}

为了安全起见,建议可以考虑使用 calloc() 函数,后面会提到它。

函数 freemalloccalloc() 都被包含在 stdlib.h 文件中。

free

函数原型:

void free(void *ptr);

我们知道在 C 语言中, 堆上的内存空间不会自动释放(Java 有自动回收机制,而 C 语言没有),直到调用 free 函数,才会释放堆上的存储空间,即 free 函数会释放指针指向的内存分配空间。

下面是函数手册查到关于 free 函数的资料:

The free() function deallocates the memory allocation pointed to by ptr.
If ptr is a NULL pointer, no operation is performed.

对于 free 函数我们要走出一个误区,不要以为调用了 free 函数,变量就变为 NULL 值了。本质是 free 函数只是割断了指针所指的申请的那块内存之间的关系,并没有改变所指的地址(本身保存的地址并没有改变)。如下示例:

char *pchar = (char *)malloc(10 * sizeof(char));
        
if (NULL != pchar) {
    strcpy(pchar, "blog");
    /* pchar所指的内存被释放,但是pchar所指的地址仍然不变 */
    free(pchar);
    
    /* 该判断没有起到防错作用,此时 pchar 并不为 NULL */
    if (NULL != pchar) {
        strcpy(pchar, "it");
        printf("pchar: %s", pchar);
    }
}

正确且安全的做法是对指针变量先进行 free 然后再将其值置为 NULL ,如下下面示例:

char *pchar = (char *)malloc(10 * sizeof(char));
        
if (NULL != pchar) {
    strcpy(pchar, "blog");
    /* pchar所指的内存被释放,但是pchar所指的地址仍然不变 */
    free(pchar);
    /* 将其置为 NULL 值 */
    pchar = NULL;
    
    /* 该判断没有起到防错作用,此时 pchar 并不为 NULL */
    if (NULL != pchar) {
        strcpy(pchar, "it");
        printf("pchar: %s", pchar);
    }
}

malloc、free 小结

1、连续内存块

malloc 函数申请的是连续的一块内存,如果所申请的内存块大于目前堆上剩余内存块,则内存分配会失败,函数返回 NULL 值。

注意:上面说的 堆上剩余内存块 不是所有剩余内存块之和,而是连续的内存。

2、双宿双飞才好

调用 malloc 函数多余 free 函数会发生内存泄漏,这个很好理解,因为申请过的内存没有被释放完。调用 malloc 函数少于 free 函数,肯定会出错。换句话说,在程序中 malloc 的使用次数务必要和 free 相等,否则必有隐患或者发生错误。

如下面的例子 free 两次指针变量就会在运行时报错: malloc: *** error for object 0x10071be90: pointer being freed was not allocated

char *pchar = (char *)malloc(10 * sizeof(char));
free(pchar);
free(pchar);

对指针变量进行 free 之后,一定要记得对其赋值为 NULL ,否则该指针就是一个野指针,这个在上面已经说明。

3、0字节的内存有毒

使用 malloc 函数也可以申请0字节的内存,该函数的返回值并不是 NULL ,而是返回一个正常的内存地址,所以如果使用这种方式申请的内存很危险,如下面的例子,指针 pchar 是一个使用 malloc 函数创建的占用0字节的内存空间的一个指针变量, if (NULL == pchar) 并没有生效,而是执行了 else 语句中的代码,执行到 strcpy(pchar, "blog") 就直接崩溃了。

char *pchar = (char *)malloc(0);
if (NULL == pchar) {
    printf("malloc 0 byte memory failed.\n");
} else {
    printf("malloc 0 byte successfully and pchar: %s.\n", pchar);
    pchar = "veryitman";
    strcpy(pchar, "blog");
    printf("pchar: %s.\n", pchar);
}

calloc、realloc、reallocf、valloc

1、calloc 函数

void * calloc(size_t count, size_t size);

在堆上,分配 n*size 个字节,并初始化为0,返回 void * 类型,返回值情况跟 malloc 一致。

函数 malloc() 和函数 calloc() 的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由 malloc() 函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据。也就是说,使用 malloc() 函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重新分配)可能会出现问题。

函数 calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零。

The calloc() function contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory.

The allocated memory is filled with bytes of value zero.

2、realloc() 函数

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

重新分配堆上的 void指针 所指的空间为 size 个字节,同时会复制原有内容到新分配的堆上存储空间。

注意,若原来的 void指针 在堆上的空间不大于 size 个字节,则保持不变。

The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns ptr. 

If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the old allocation, and returns a pointer to the allocated memory.  

If ptr is NULL, realloc() is identical to a call to malloc() for size bytes.  

If size is zero and ptr is not NULL, a new, minimum sized object is allocated and the original object is freed.  

When extending a region allocated with calloc(3), realloc(3) does not guarantee that the additional memory is also zero-filled.

3、reallocf() 函数

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

reallocf() 函数是由 FreeBSD 实现的,它会在任何情况下释放输入的指针(即使是再分配失败之后)。 reallocf() 一样会调用 realloc 函数,但是只有我们在获得空的指针之后才会调用 free 函数。

下面是 reallocf 函数具体的实现部分:

void * reallocf(void *p, size_t size)
{
    void *ptr = realloc(p, size);
    if (!p) {
        free(p);
    }
    return ptr;
}
The reallocf() function is identical to the realloc() function, except that it will free the passed pointer when the requested memory cannot be allocated.

This is a FreeBSD specific API designed to ease the problems with traditional coding styles for realloc causing memory leaks in libraries.

4、valloc() 函数

void * valloc(size_t size);

这个函数是最少见也是最少用的一个函数。

mallocrealloc 返回的是以8字节对齐的内存地址,在64bits上是16字节对齐。然而 memalignvalloc 可以更大的粒度进行字节对齐。

valloc 是一个废弃的函数,分配 size 大小的字节,返回已分配的内存地址指针,其内存地址将是页大小(page size)的倍数,如果分配失败返回 NULL

The valloc() function allocates size bytes of memory and returns a pointer to the allocated memory. 

The allocated memory is aligned on a page boundary.

锄禾日当午,汗滴禾下土,五一节快乐~

双宿双飞的 malloc 和 free


以上所述就是小编给大家介绍的《双宿双飞的 malloc 和 free》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

写给大家看的设计书(第4版)

写给大家看的设计书(第4版)

Robin Williams / 苏金国、李盼 / 人民邮电出版社 / 2016-1 / 59.00元

畅销设计入门书最新版,让每个人都能成为设计师 在这个创意无处不在的时代,越来越多的人成为设计师。简历、论文、PPT、个人主页、博客、活动海报、给客人的邮件、名片……,处处都在考验你的设计能力。 美术功课不好?没有艺术细胞?毫无设计经验? 没关系!在设计大师RobinWilliams看来,设计其实很简单。在这部畅销全球多年、影响了一代设计师的经典著作中,RobinWilliams将......一起来看看 《写给大家看的设计书(第4版)》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具