asn1c:为什么我需要释放这个结构的这个成员?

asn1c: Why do i need to free this member of this struct?

目前我正在尝试了解 asn1c 编译器。我现在正在阅读 PDF https://lionet.info/asn1c/asn1c-usage.pdf 。在 2.1.7 释放目标结构 部分中是这样的示例:

/*
1. Rectangle_t is defined within my_figure
*/
struct my_figure {
    Rectangle_t rect;
} *mf = ...;

/*
 * Freeing the Rectangle_t* without freeing the mf->rect area.
*/
asn_DEF_Rectangle.free_struct( &asn_DEF_Rectangle, &mf->rect,   1 /* !free */ );

我会将 rect 视为嵌入同一内存块中的 struct my_figure 的一部分。那么,为什么我需要用那个函数释放那个结构?当它不释放内存块时,这个功能有什么用?

Rectangle_t 的定义如下:

RectangleTest DEFINITIONS ::= BEGIN

Rectangle ::= SEQUENCE {
    height INTEGER,
    width INTEGER
}

END

生成的header

/*
 * Generated by asn1c-0.9.24 (http://lionet.info/asn1c)
 * From ASN.1 module "RectangleTest"
 *  found in "../Rectangle.asn1"
 */

#ifndef _Rectangle_H_
#define _Rectangle_H_


#include <asn_application.h>

/* Including external dependencies */
#include <INTEGER.h>
#include <constr_SEQUENCE.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Rectangle */
typedef struct Rectangle {
    INTEGER_t    height;
    INTEGER_t    width;

    /* Context for parsing across buffer boundaries */
    asn_struct_ctx_t _asn_ctx;
} Rectangle_t;

/* Implementation */
extern asn_TYPE_descriptor_t asn_DEF_Rectangle;

#ifdef __cplusplus
}
#endif

#endif  /* _Rectangle_H_ */
#include <asn_internal.h>

我认为您没有正确理解免费功能的工作原理。 free 获取指向您为结构分配的区域的指针,然后让操作系统知道它不再是分配的内存部分。例如,如果您创建一个向量结构:

typedef struct Vector2 {
    float x,y;
} Vector2; 

// Somewhere in an executing section of code for example you do this
Vector2 *vec;
vec = (Vector2*) malloc(sizeof(Vector2));

现在,正如您所说,Vector2 只是内存中某处的顺序数据(如果您想在称为 heap 的区域中更具体)。如果你能看到它,它看起来像:

堆内存:... |浮动 x |浮动y | ...

如果你想释放一个分配的 Vector2,你只需要调用传递它的指针的释放函数:

free(vec);

这是最简单的示例,因为除了调用自由函数之外没有更多必要的处理。那是因为 struct Vector2 没有指针!如果结构更复杂并且有一些指针,在释放洞之前,你必须释放它的所有指针。下一个例子是一个列表实现,其中每个节点都指向下一个节点,直到最后一个节点指向 NULL。

typedef struct Node {
    int data;
    struct Node* next;
} Node;

//Returns a new node
Node* newNode(){
     Node* node = (Node*) malloc(sizeof(Node));
     node->next = NULL;
     return node;
}

//Adds new element at tail
Node* addAtTail(Node* list){
     if(list->next != NULL)
          return addAtTail(list->next);

     list->next = newNode();
     return list->next;
}

下一个列表将如何在您的堆内存中?

Node* example_list = newNode();
addAtTail(example_list);
addAtTail(example_list);
addAtTail(example_list);

这个列表就是下面的列表:A -> B -> C -> D -> NULL

这是您的堆内存:

...{ 整数数据 |下一个节点 } ... { int 数据 |下一个节点 } ... { int 数据 |下一个节点 } ... { int 数据 |下一个节点* } ...

好的。如果您只调用以下代码行:free(example_list);,您的堆内存将如何? example_list 是 "A" 节点(第一个节点)因此你将 只有 个空闲节点 "A"!这就是你现在的堆内存:

...{ ----------freed-------- } ... { int 数据 |下一个节点 } ... { int 数据 |下一个节点 } ... { int 数据 |下一个节点* } ...

所有那些您没有释放的节点会怎样? :D

内存泄漏 是正确的术语。你记忆中的那些可怜的数据没有指向它们的指针,因此它们永远丢失在你的记忆中,只是占用space!在大多数应用程序中,您不会注意到任何差异,但在关键和正在进行的应用程序中,拥有一个无内存泄漏的应用程序非常重要,因此保持代码不泄漏内容是非常好的做法。

也许 Rectangle_t 有一些指针需要以特殊且独特的方式释放 :) 我们示例列表的特殊释放函数如下所示:

void freeList(Node* node){
     if(node == NULL)
          return;
     free_list(node->next);
     free(node);
}

如果不调用释放结构体Node的特殊函数,会出现内存泄漏,所以有必要。我想这与 Rectangle_t

的原因相同

我不是 ASN.1 或此编译器方面的专家,但在阅读文档后,我想我明白发生了什么。是的,在这种情况下,您不需要调用 "free_struct" 因为 "rect" 不是由解码器分配的,它不包含任何分配的成员。所以在某种程度上这是一个不好的例子,他们把它搞得太简单了。文档不是很好,但是示例所做的是解释 "free_struct" 最后一个参数的用法。通常,在完成解码结构后(或者如果有错误),您将释放它。 "free_struct" 将始终释放解码器在解码结构内分配的内存,但是 "free_struct" 最后一个参数的值告诉它是否应该释放解码结构本身。

看下面的例子:

Rectangle_t *
simple_deserializer(const void *buffer, size_t buf_size) {
    asn_dec_rval_t rval;
    Rectangle_t *rect = 0; /* Note this 01! */
    rval = asn_DEF_Rectangle.ber_decoder(0,
        &asn_DEF_Rectangle,
        (void **)&rect, /* Decoder moves the pointer */
        buffer, buf_size, 0);
    if (rval.code == RC_OK) {
        return rect; /* Decoding succeeded */
    }
    else {
        /* Free partially decoded rect */
        asn_DEF_Rectangle.free_struct(&asn_DEF_Rectangle, rect,
            0);
        return 0;
    }
}

如您所见,当您调用解码器时,您传递了一个指向解码结构的指针。还要注意指向解码结构的指针被初始化为 0。我相信这告诉解码器没有解码结构,它需要为一个分配内存。

你也可以像这样自己分配内存。

Rectangle_t *
simple_deserializer(const void *buffer, size_t buf_size) {
    asn_dec_rval_t rval;
    Rectangle_t *rect;
    if ((rect = malloc(sizeof(Rectangle_t)) == 0)
        return 0;
    rval = asn_DEF_Rectangle.ber_decoder(0,
        &asn_DEF_Rectangle,
        (void **)&rect, //the decoder realizes that rect is not 0 and does not allocated memory for it.
        buffer, buf_size, 0);
    if (rval.code == RC_OK) {
        return rect; /* Decoding succeeded */
    }
    else {
        /* Free partially decoded rect */
        asn_DEF_Rectangle.free_struct(&asn_DEF_Rectangle, rect,
            1);
        free(rect);
        return 0;
    }
}

在这种情况下,解码器意识到指向解码结构的指针不为 0,因此它假定存在解码结构并且不为其分配内存。

现在举个例子,就像你给的那个(你有一个 "Rectangle_t" 而不是指向 "Rectangle_t" 的指针):

struct my_figure { /* The custom structure */
    int flags; /* <some custom member> */
    /* The type is generated by the ASN.1 compiler */
    Rectangle_t rect;
    /* other members of the structure */
};

int
process_data(const void *buffer, size_t buf_size) {
    asn_dec_rval_t rval;
    my_figure mf;
    Rectangle_t prect;
    prect = &mf.rect;
    rval = asn_DEF_Rectangle.ber_decoder(0,
        &asn_DEF_Rectangle,
        (void **)&prect, //the decoder realizes that prect is not 0 and assumes it is pointing to a Rectangle_t and does not allocated memory for it.
        buffer, buf_size, 0);
    if (rval.code == RC_OK) {
        //Add code here to process the decoded data.
        return 0; //return 0 to indicate data processed successfully.
    }
    else {
        /* Free partially decoded rect */
        asn_DEF_Rectangle.free_struct(&asn_DEF_Rectangle, prect,
            1);
        return 1; //return a non-zero value to indicate data was not processed successfully.
    }
}

在这种情况下,您需要传递 1(或者可能是任何非零值,我不知道)告诉 "free_struct" 不要释放 prect 指向的内存。