2

I currently have the following code in my program:

struct mystruct
{
    int x;
    struct y *list[10];
}

mystruct *h1=(mystruct *)malloc(sizeof(mystruct));

I want to declare the array list dynamically (using malloc()) when I declare the structure. Could anyone tell me how to go about this?

Dan Fego
  • 13,644
  • 6
  • 48
  • 59
hektor
  • 1,017
  • 3
  • 14
  • 28

2 Answers2

1

You need to do it explicitly. I normally do this with a wrapper function like this.

Edit: Now there's a complete working example at the bottom.

struct mystruct *mystruct_init()
{
    struct mystruct *mystruct = calloc(1, sizeof(*mystruct));

    // loop through and allocate memory for each element in list
    for (int i = 0; i < 10; i++) {
        mystruct->list[i] = calloc(1, sizeof(*(mystruct->list[i])));
    }

    return mystruct;
}

That way, you just call this in your code:

struct mystruct *h1 = mystruct_init();

You'll also want to write a corresponding mystruct_free() function.

Here's a working example (for me):

#include <stdlib.h>
#include <stdio.h>

struct y {
    int a;
};

struct mystruct {
    int x;
    struct y **list;
    int list_length;
};

struct mystruct *mystruct_init()
{
    struct mystruct *mystruct = calloc(1, sizeof(*mystruct));

    // loop through and allocate memory for each element in list
    mystruct->list_length = 10;
    mystruct->list = calloc(1, sizeof(struct y *) * mystruct->list_length);
    for (int i = 0; i < mystruct->list_length; i++) {
        mystruct->list[i] = calloc(1, sizeof(struct y));
    }

    return mystruct;
}

void mystruct_free(struct mystruct *mystruct)
{
    for (int i = 0; i < mystruct->list_length; i++) {
        free(mystruct->list[i]);
    }

    free(mystruct->list);
    free(mystruct);
}

int main(int argc, char *argv[])
{
    struct mystruct *h1 = mystruct_init();
    h1->x = 6;
    printf("%d\n", h1->x);
    mystruct_free(h1);
    return 0;
}
Dan Fego
  • 13,644
  • 6
  • 48
  • 59
  • How can you reference y ? I suppose that you made a mistake :) – DonCallisto Jan 31 '12 at 18:50
  • Just to add to @DanFego's code, be sure to `free()` the memory got from `malloc()` after you are done. There are many threads on Stack Overflow explaining memory leaks caused due to returning a local variable from a function. One link: http://stackoverflow.com/a/423206/630866 – Bhaskar Jan 31 '12 at 19:01
  • @DanFego: i modified the program according to your suggestion. However i am getting a segmentation fault when the execution hits this line " mystruct->list[i] = calloc(1, sizeof(*(mystruct->list[i]));" what could be going wrong? should i paste my full code? – hektor Jan 31 '12 at 19:47
  • @hektor I might have made a mistake with my `sizeof`. Try changing it to `sizeof(struct y)` and see if it works. :) – Dan Fego Jan 31 '12 at 20:02
  • @hektor I just quickly mocked up a program with no faults, though I fixed a couple more typos above, including on the line mentioned. Perhaps you corrected it with a parentheses in the wrong place? – Dan Fego Jan 31 '12 at 20:07
  • i changed the line to mystruct->list[i] = calloc(1, sizeof(struct y)); Still end up with the segmentation fault :( – hektor Jan 31 '12 at 20:15
  • @hektor: updated with my own working example. Perhaps try that? – Dan Fego Jan 31 '12 at 20:19
  • @DanFego : Does this match the program you made up struct mystruct *mystruct_init() { struct mystruct *mystruct = calloc(1, sizeof(*mystruct)); // loop through and allocate memory for each element in list for (int i = 0; i < 10; i++) { mystruct->list[i] = calloc(1, sizeof(struct y)); } return mystruct; } – hektor Jan 31 '12 at 20:20
  • @DanFego: Since i have to re-size my array at a later point of time in the program, i have declared list is struct node **list. Will the current implementation hold in this case as well? – hektor Jan 31 '12 at 20:38
  • @hektor yes, but you'll need to change your initialization constant (10) and the same one in the free function. So you'll want to store a variable in the `struct` that contains the number of elements in the list so you know how much to free. – Dan Fego Jan 31 '12 at 20:43
  • @DanFego: i changed the initialization to struct y **list and use the same wrapper function. It is giving me a segmentation fault at this line hashtable->list[i] = calloc(1, sizeof(*(hashtable->list[i]))); Sorry for disturbing you for so long, but it'd be really nice if you could point out my mistake. – hektor Jan 31 '12 at 20:52
  • @DanFego: if i want to increase the size of 'list',how do i use realloc to accomplish that? should i do a realloc on mystruct as well as list? If so, could you tell me how to go about it? – hektor Feb 01 '12 at 20:22
  • @hektor At this point you should search SO for this new question, and if you can't find an answer, post a new question for it. :) – Dan Fego Feb 01 '12 at 20:24
  • @DanFego: i posted a new question. However i have a doubt with the code that you provided, especially with the parameter '1' in the calloc function. Could you explain that? – hektor Feb 01 '12 at 21:09
  • @hektor `calloc()` takes two parameters: the number of blocks to allocate and set to `0`, and the size of each block. So it's grabbing `1` block of the given size, set to `0`. – Dan Fego Feb 01 '12 at 21:11
1

If you're only going to port to machines (compilers) that support C99, then you could consider using the 'flexible array members', which is the portable version of the 'struct hack'.

§6.7.2.1 Structure and union specifiers

¶16 As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. With two exceptions, the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length.106) Second, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

¶17 EXAMPLE Assuming that all array members are aligned the same, after the declarations:

struct s { int n; double d[]; };
struct ss { int n; double d[1]; };

the three expressions:

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

have the same value. The structure struct s has a flexible array member d.

106) The length is unspecified to allow for the fact that implementations may give array members different alignments according to their lengths.


In context, that means you might write:

typedef struct mystruct
{
    int x;
    struct y *list[];
} mystruct;  // Note that this is necessary; C++ automatically recognizes mystruct; C does not.

To allocate space, you might use:

mystruct *h1 = (mystruct *)malloc(sizeof(mystruct) + 10 * sizeof(struct y *));

This allocates a mystruct with enough space for 10 pointers in the array. You can later resize the memory with:

mystruct *new_h1 = (mystruct *)realloc(h1, sizeof(mystruct) + 20 * sizeof(struct y *));
if (new_h1 == 0)
    ...handle out of memory error, but note that h1 is still valid...
h1 = new_h1; // Safe

(Note that I carefully do not assign to h1 on the reallocation; to do so would leak memory if you get a memory allocation failure.)

You can reference them as if the array was there:

h1->list[0] = ...;

Note that you cannot have an array of mystruct, but you can have an array of pointers to mystruct. It is also up you to keep tabs on the size of the array; you'd normally do something like have one of the fixed members record the size allocated.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278