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

时间:2016-07-11 18:37:43

标签: c struct asn.1

目前我尝试了解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

生成的标题

/*
 * 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>

2 个答案:

答案 0 :(得分:5)

I think you didn't understand correctly how the free function works. free gets a pointer pointing to the region you allocated for your struct and then lets the operational system know it is not an allocated section of memory anymore. For example if you create a vector struct:

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));

By now, as you said, the Vector2 is just sequential data somewhere in memory (if you want to be more specific in a region called heap). If you could see it, it would look like:

HEAP-MEMORY: ... | float x | float y | ...

if you want to free an allocated Vector2 you simply need to call the free function passing its pointer:

free(vec);

This is the simplest example because there is no more treatment necessary rather than calling the free function. That's because the struct Vector2 has no pointers! If the struct is more complex and has some pointers, before freeing the hole thing, you must free all it's pointers. The next example is a list implementation, in which every node points to the next node until the last node points to 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;
}

How the next list will be in your heap memory?

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

This list is the following list: A -> B -> C -> D -> NULL

This is your HEAP-MEMORY:

...{ int data | Node* next } ... { int data | Node* next } ... { int data | Node* next } ... { int data | Node* next } ...

Okay. How will be your heap memory if you only call the following line of code: free(example_list);? example_list is the "A" node (the first node) therefore you will only free node "A"! That's how your heap memory is right now:

...{ ---------freed-------- } ... { int data | Node* next } ... { int data | Node* next } ... { int data | Node* next } ...

What happens with all those nodes you didn't free? :D

MEMORY LEAK is the correct term for that. Those poor data in your memory have no pointers pointing to them, therefore they are lost forever in your memory and are just occupying space! In most applications you won't notice any difference, but on critical and ongoing applications it is very important to have a no memory leaking application, so it is very good practice to keep your code not leaking stuff.

Maybe Rectangle_t has some pointers that need to be freed in a special and unique way :) The special free function for our example list would be like this:

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

If you do not call the special function to free the struct Node, there will be memory leak, that's why it is necessary. I suppose this is the same reason for Rectangle_t

答案 1 :(得分:2)

I am not an expert on ASN.1 or on this compiler but after reading the documentation, I think I understand what is going on. Yes, in this case, you do not need to call "free_struct" because "rect" was not allocated by a decoder and it does not contain any members that were. So in a way this is a bad example, they made it too simple. The documentation is not great, but what the example is doing is to explain the use for the last argument to "free_struct". In general after you finish with the decoded structure (or if there is an error) you free it. "free_struct" will always free the memory allocated by the decoder inside the decoded structure, but the value of the last argument to "free_struct" tells it whether it should free the decoded structure itself.

Look at the example below:

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;
    }
}

As you can see, when you call the decoder you pass a pointer to a pointer to the decoded structure. Notice also that the pointer to the decoded structure is initialized to 0. I believe this tells the decoder that there is no decoded structure and it needs to allocate memory for one.

You could also have allocated the memory yourself, like this.

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;
    }
}

In this case, the decoder realizes the pointer to the decoded structure is not 0 so it assumes there is a decoded structure and does not allocated memory for it.

Now for an example, like the one you gave (where you have a "Rectangle_t" instead of a pointer to a "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.
    }
}

In this case, you need to pass 1 (or maybe any non-zero value, I don't know) to tell "free_struct" not to free the memory pointed to by prect.