Allocate Pointer and pointee at once

穿精又带淫゛_ 提交于 2019-12-11 02:34:48

问题


If I want to reduce malloc()s (espacially if the data is small and allocated often) I would like to allocate the pointer and pointee at once.

If you assume something like the following:

struct entry {
    size_t      buf_len;
    char        *buf;
    int         something;
};

I would like to allocate memory in the following way (don't care about error checking here):

size_t buf_len  = 4;                // size of the buffer
struct entry *e = NULL;

e = malloc( sizeof(*e) + buf_len ); // allocate struct and buffer
e->buf_len  = buf_len;              // set buffer size
e->buf      = e + 1;       // the buffer lies behind the struct

This could even be extende, so that a whole array is allocated at once.

How would you assess such a technuique with regard to:

  • Portability
  • Maintainability / Extendability
  • Performance
  • Readability

Is this reasonable? If it is ok to use, are there any ideas on how to design a possible interface for that?


回答1:


You could use a flexible array member instead of a pointer:

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[];
};

// ...
struct entry *e = malloc(sizeof *e  + buf_len);
e->buf_len = buf_len;

Portability and performance are fine. Readability: not perfect but good enough.

Extendability: you can't use this for more than one member at a time, you'd have to fall back to your explicit pointer version. Also, the explicit pointer version means that you have to muck around to ensure correct alignment if you use it with a type that doesn't have an alignment of 1.

If you are seriously thinking about this I'd consider revisiting your entire data structure's design to see if there is another way of doing it. (Maybe this way is actually the best way, but have a good think about it first).




回答2:


As to portability, I am unaware of any issues, as long as the sizes are found via suitable calls to sizeof(), as in your code.

Regarding maintainability, extendability and readability, you should certainly wrap allocation and de-allocation in a well-commented function. Calls to...

entry *allocate_entry_with_buffer();
void deallocate_entry_with_buffer(entry **entry_with_buffer);

...do not need to know implementation details of how the memory actually gets handled. People use stranger things like custom allocators and memory pools quite frequently.

As for speed, this is certainly faster than making lots of small allocations. I used to allocate whole 2D matrices with a similar strategy...




回答3:


It should work, but in fact you are using a pointer for a useless indirection. Windows API (for example) uses another method for variable size structs : the variable size buffer is last in struct and is declared to be char buf[1].

Your struct would become :

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[1];
};

The allocation is (still no error checking) :

size_t buf_len  = 4;                // size of the buffer
struct entry *e;

e = malloc( sizeof(*e) + buf_len  - 1); // struct already has room for 1 char
e->buf_len  = buf_len;              // set buffer size

That's all e.buf is guaranteed to be a char array of size buf_len.

That way ensures that even if the variable part was not a character array but a int, long, or anything array, the alignement would be given by the last element being a array of proper type and size 1.




回答4:


For starters, the line:

e->buf      = e + sizeof(*e);       // the buffer lies behind the struct

Should be:

e->buf      = e + 1;       // the buffer lies behind the struct

This is because e + 1 will be equal to the address at the end of the structure. As you have it, it will only be the number of bytes into the structure equal to the number of bytes in a pointer.

And, yes, it's reasonable. However, I prefer this approach:

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[1];
};

This way, you don't mess with the pointers. Just append as many bytes as needed, and they will grow the size of your buf array.

Note: I wrote a text editor using an approach similar to this but used a Microsoft c++ extension that allowed me to declare the last member as char buf[]. So it was an empty array that was exactly as long as the number of extra bytes I allocated.




回答5:


seems fine to me - put comments in though

Or you could do this - which is quite common

struct entry {
    size_t      buf_len;
    int         something;
    char        buf;
};

ie make the struct itself variable length. and do

size_t buf_len  = 4;                // size of the buffer
struct entry *e = NULL;
//  check that it packs right
e = malloc(sizeof(size_t) + sizeof(int) + buf_len ); // allocate struct and buffer
e->buf_len  = buf_len;              // set buffer size
...... later
printf(&e.buf);


来源:https://stackoverflow.com/questions/29088176/allocate-pointer-and-pointee-at-once

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!