Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to include a dynamic array INSIDE a struct in C?

I have looked around but have been unable to find a solution to what must be a well asked question. Here is the code I have:

 #include <stdlib.h>

struct my_struct {
    int n;
    char s[]
};

int main()
{
    struct my_struct ms;
    ms.s = malloc(sizeof(char*)*50);
}

and here is the error gcc gives me: error: invalid use of flexible array member

I can get it to compile if i declare the declaration of s inside the struct to be

char* s

and this is probably a superior implementation (pointer arithmetic is faster than arrays, yes?) but I thought in c a declaration of

char s[]

is the same as

char* s
like image 495
Tom Avatar asked Jan 13 '10 23:01

Tom


People also ask

Can you put arrays in structs?

A structure may contain elements of different data types – int, char, float, double, etc. It may also contain an array as its member. Such an array is called an array within a structure. An array within a structure is a member of the structure and can be accessed just as we access other elements of the structure.

Can you malloc inside a struct?

The malloc() function is used for the declaration of the dynamic memory. An array of a struct can be declared either using the static memory or dynamic memory, in this write-up, we will discuss the array of structs using the malloc() function.

Can we create array dynamically in C?

We can create an array of pointers also dynamically using a double pointer. Once we have an array pointers allocated dynamically, we can dynamically allocate memory and for every row like method 2.


3 Answers

The way you have it written now , used to be called the "struct hack", until C99 blessed it as a "flexible array member". The reason you're getting an error (probably anyway) is that it needs to be followed by a semicolon:

#include <stdlib.h>  struct my_struct {     int n;     char s[]; }; 

When you allocate space for this, you want to allocate the size of the struct plus the amount of space you want for the array:

struct my_struct *s = malloc(sizeof(struct my_struct) + 50); 

In this case, the flexible array member is an array of char, and sizeof(char)==1, so you don't need to multiply by its size, but just like any other malloc you'd need to if it was an array of some other type:

struct dyn_array {      int size;     int data[]; };  struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int)); 

Edit: This gives a different result from changing the member to a pointer. In that case, you (normally) need two separate allocations, one for the struct itself, and one for the "extra" data to be pointed to by the pointer. Using a flexible array member you can allocate all the data in a single block.

like image 162
Jerry Coffin Avatar answered Sep 29 '22 10:09

Jerry Coffin


You need to decide what it is you are trying to do first.


If you want to have a struct with a pointer to an [independent] array inside, you have to declare it as

struct my_struct {    int n;    char *s; };  

In this case you can create the actual struct object in any way you please (like an automatic variable, for example)

struct my_struct ms; 

and then allocate the memory for the array independently

ms.s = malloc(50 * sizeof *ms.s);   

In fact, there's no general need to allocate the array memory dynamically

struct my_struct ms; char s[50];  ms.s = s; 

It all depends on what kind of lifetime you need from these objects. If your struct is automatic, then in most cases the array would also be automatic. If the struct object owns the array memory, there's simply no point in doing otherwise. If the struct itself is dynamic, then the array should also normally be dynamic.

Note that in this case you have two independent memory blocks: the struct and the array.


A completely different approach would be to use the "struct hack" idiom. In this case the array becomes an integral part of the struct. Both reside in a single block of memory. In C99 the struct would be declared as

struct my_struct {    int n;    char s[]; };  

and to create an object you'd have to allocate the whole thing dynamically

struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s); 

The size of memory block in this case is calculated to accommodate the struct members and the trailing array of run-time size.

Note that in this case you have no option to create such struct objects as static or automatic objects. Structs with flexible array members at the end can only be allocated dynamically in C.


Your assumption about pointer aritmetics being faster then arrays is absolutely incorrect. Arrays work through pointer arithmetics by definition, so they are basically the same. Moreover, a genuine array (not decayed to a pointer) is generally a bit faster than a pointer object. Pointer value has to be read from memory, while the array's location in memory is "known" (or "calculated") from the array object itself.

like image 23
AnT Avatar answered Sep 29 '22 09:09

AnT


The use of an array of unspecified size is only allowed at the end of a structure, and only works in some compilers. It is a non-standard compiler extension. (Although I think I remember C++0x will be allowing this.)

The array will not be a separate allocation for from the structure though. So you need to allocate all of my_struct, not just the array part.

What I do is simply give the array a small but non-zero size. Usually 4 for character arrays and 2 for wchar_t arrays to preserve 32 bit alignment.

Then you can take the declared size of the array into account, when you do the allocating. I often don't on the theory that the slop is smaller than the granularity that the heap manager works in in any case.

Also, I think you should not be using sizeof(char*) in your allocation.

This is what I would do.

struct my_struct {
    int nAllocated;
    char s[4]; // waste 32 bits to guarantee alignment and room for a null-terminator
};

int main()
{
    struct my_struct * pms;
    int cb = sizeof(*pms) + sizeof(pms->s[0])*50;
    pms = (struct my_struct*) malloc(cb);
    pms->nAllocated = (cb - sizoef(*pms) + sizeof(pms->s)) / sizeof(pms->s[0]);
}
like image 33
John Knoeller Avatar answered Sep 29 '22 09:09

John Knoeller