首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >垃圾堆,也就是最简单的内存管理工具

垃圾堆,也就是最简单的内存管理工具
EN

Code Review用户
提问于 2019-10-03 21:59:35
回答 1查看 79关注 0票数 4

其思想是在main开头插入垃圾堆,使用gmallocgcallocgrealloc包装器,并在main末尾释放所有分配的内存。在为电子判断系统编写程序时,它有助于避免内存泄漏。希望听取关于如何改进代码(C语言初学者)的想法和建议的一般反馈意见。

main.c

代码语言:javascript
复制
/*
Example of usage of garbage heap.
No free() call but no memory leak.
*/

#include "garbage_heap.h"


int main() {
    init_garbage_heap();
    for (int i = 0; i < 1000; ++i)
    {
        char* str = gmalloc(1000*sizeof(char));
        str = grealloc(str, 2000*sizeof(char));
    }
    free_garbage_heap();
    return 0;
}

garbage_heap.h

代码语言:javascript
复制
#ifndef GARBAGE_HEAP_H
#define GARBAGE_HEAP_H

#include <stddef.h>

void init_garbage_heap (void);
void free_garbage_heap (void);
void* gmalloc (size_t sizemem);
void* gcalloc (size_t number, size_t size);
void* grealloc (void* ptrmem, size_t sizemem);

#endif // GARBAGE_HEAP_H

garbage_heap.c

代码语言:javascript
复制
#include <stdlib.h>

#include "garbage_heap.h"


typedef struct
{
    void** buffer;
    int len;
    int capacity;
}
GarbageHeap;


static GarbageHeap garbage_heap;


void init_garbage_heap(void)
{
    int len = 32;
    garbage_heap.len = 0;
    garbage_heap.capacity = len;
    garbage_heap.buffer = (void**)malloc(sizeof(void*)*len);
    for (int i = 0; i < len; ++i)
        garbage_heap.buffer[i] = NULL;
}


void free_garbage_heap(void)
{
    for (int i = 0; i < garbage_heap.len; ++i)
        if (garbage_heap.buffer[i] != NULL)
            free(garbage_heap.buffer[i]);
    free(garbage_heap.buffer);
}


void resize_garbage_heap(void)
{
    garbage_heap.capacity *= 2;
    garbage_heap.buffer = (void**)realloc(garbage_heap.buffer, sizeof(void*) * garbage_heap.capacity);
    for (int i = garbage_heap.len; i < garbage_heap.capacity; ++i)
        garbage_heap.buffer[i] = NULL;
}


void* gmalloc(size_t sizemem)
{
    if (garbage_heap.len == garbage_heap.capacity)
        resize_garbage_heap();
    garbage_heap.buffer[garbage_heap.len] = malloc(sizemem);
    ++garbage_heap.len;
    return garbage_heap.buffer[garbage_heap.len - 1];
}


void* gcalloc(size_t number, size_t size)
{
    if (garbage_heap.len == garbage_heap.capacity)
        resize_garbage_heap();
    garbage_heap.buffer[garbage_heap.len] = calloc(number, size);
    ++garbage_heap.len;
    return garbage_heap.buffer[garbage_heap.len - 1];
}


void* grealloc(void* ptrmem, size_t sizemem)
{
    if (ptrmem == NULL)
        return gmalloc(sizemem);
    int i = 0;
    while (garbage_heap.buffer[i] != ptrmem)
        ++i;
    void* tmp_ptr = realloc(ptrmem, sizemem);
    if (tmp_ptr != NULL)
        garbage_heap.buffer[i] = tmp_ptr;
    return tmp_ptr;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2019-10-04 16:00:05

有趣的概念,我猜C不是您的第一种编程语言,您的第一语言不需要显式的内存管理。

代码非常好,但是这里有一些需要考虑的事项:

良好的编程实践

在大多数if语句和循环中,问题中的代码很难维护,因为它没有为贯穿代码的每条路径提供复杂的语句。复杂语句是由{}封装在C语言中的代码块。

一个复杂语句的例子是:{语句;语句;}

缺少这种良好编程实践的代码的一个例子是

代码语言:javascript
复制
void free_garbage_heap(void)
{
    for (int i = 0; i < garbage_heap.len; ++i)
        if (garbage_heap.buffer[i] != NULL)
            free(garbage_heap.buffer[i]);
    free(garbage_heap.buffer);
}

假设维护代码的人必须在循环中的if语句中添加一条语句,并且他们希望快速地这样做,或者他们来自一种定位的编程语言,而不是使用大括号来表示一个复杂的语句。如果他们直接在free(garbage_heap.buffer[i]);下面添加它而不添加大括号,那么不仅新语句不在if语句中,新语句甚至不在for循环中。

在C和C++等语言中,以下代码是一种更安全的编程实践:

代码语言:javascript
复制
void free_garbage_heap(void)
{
    for (int i = 0; i < garbage_heap.len; ++i)
    {
        if (garbage_heap.buffer[i] != NULL)
        {
            free(garbage_heap.buffer[i]);
        }
    }
    free(garbage_heap.buffer);
}

添加新语句的位置变得更加清晰。

函数init_garbage_heap的可能改进

init_garbage_heap()中要注意的第一件事是,在调用void* malloc(int number_of_bytes_to_allocate)之后没有错误检查。对malloc()的调用可能失败,如果调用失败,则返回的值为NULL。如果malloc()返回一个错误,那么如果代码继续处理,那么garbage_heap.buffer[i] = NULL;的for循环中就会出现内存访问错误,这可能会导致程序崩溃(退出不当)。在C++中,如果new()失败,它将引发异常,C编程语言没有异常,因此需要进行测试。

下面的代码中要注意的第二件事是,在(void**)的C99或更高版本的C中,在原始的C malloc()返回char*和必需的强制转换中,不需要强制转换,因为它现在返回了void.it不需要。

代码语言:javascript
复制
void init_garbage_heap(void)
{
    int len = 32;
    garbage_heap.len = 0;
    garbage_heap.capacity = len;
    garbage_heap.buffer = (void**)malloc(sizeof(void*)*len);
    for (int i = 0; i < len; ++i)
        garbage_heap.buffer[i] = NULL;
}

要注意的第三件事是,garbage_heap.buffer的类型可以改变,所以如果使用sizeof(*garbage_heap.buffer) * len而不是说明sizeof(void*)*len,可能会更好。这样,如果buffer的类型发生变化,代码只需要更改声明buffer的位置,而不需要在多个地方更改。

garbage_heap.buffer的内容初始化为NULL的for循环可以替换为调用(*ptr,int值,大小_));

代码语言:javascript
复制
    memset(garbage_heap.buffer, 0, sizeof(*garbage_heap.buffer) * len);

这可能会比for循环执行更快的速度,这取决于memset()的编写方式。它也是用代码的其余部分编写的。

如果将对malloc()的调用替换为对calloc()的调用,则不需要for循环或对memset()的调用,因为calloc()将将被返回的内存内容设置为NULL。

`init_garbage_heap的可能改进版本(Void):

代码语言:javascript
复制
void init_garbage_heap(void)
{
    int len = 32;

    garbage_heap.len = 0;
    garbage_heap.capacity = len;
    garbage_heap.buffer = calloc(sizeof(*garbage_heap.buffer), len);
    if (!garbage_heap.buffer)
    {
        fprintf(stderr, "Unable to allocate memory for garbage_heap.buffer, exiting program");
        exit(EXIT_FAILURE);
    }
}

尚不清楚len为什么要被分配32,最好使用一个符号常量而不是32。如果这些例程的用户能够设置初始分配容量,也可能会更好。如果初始缓冲区大小大于32,则更好,以减少调整大小的需要。

间距

如果函数中有垂直间距,garbage_heap.c中的代码可能更易读。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/230119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档