1

What is wrong in the below way to make a variable size struct?

  struct tode{
      int g;
      int *p;
  };

  struct tode *lp = malloc(sizeof(struct tode) + (10 * sizeof(int)));

Note: This is completely wrong but created a confusion in my head so tried to discuss

codey modey
  • 983
  • 2
  • 10
  • 23

3 Answers3

3

p in your structure is a pointer to an int, not an array. So it won't compile. You should use zero-length array or flexible array member to dynamically allocate memory for it. However, it must be the last struct member. Here is how you declare it.

struct tode {
    int g;
    int p[]; // p is a zero length array here, not a pointer
};

// sizeof(tode) is 4 on a 32-bit machine

It is one of the few cases where you can declare a zero-length array (other I recall is when you declare an extern array). Since a zero-length member is an incomplete type, you must first allocate memory, and then you can do any operation with it.

struct tode *lp = malloc(sizeof(tode) + 10*sizeof(int));

// after you are done with lp

free(lp);

Here, the 10*sizeof(int) part in the malloc argument dynamically allocates the array. sizeof(tode) doesn't take into account p because it's an incomplete type. After the above statement, p is an array of 10 ints.

However, if you did this

struct tode {
    int g;
    int *p; // p is a pointer
};

// sizeof(tode) is 8 on a 32-bit machine

Then you would dynamically allocate the structure as

struct tode *lp = malloc(sizeof(tode));

// lp->p is of type (int *) so it can contain any int address

lp->p = malloc(10 * sizeof(int));

// after you are done with lp

free(lp->p);
free(lp);

You can also allocate and initialize your structure on the stack using designated initializer (C99) as showed by Jens

struct tode lv = {.g = 10, .p = malloc(10 * sizeof(int))}; 

However, before the variable lv goes out of scope, you should free the memory pointed to by lv.p as

free(lv.p);
Community
  • 1
  • 1
ajay
  • 9,402
  • 8
  • 44
  • 71
  • pointers can behave like arrays int *A; A = malloc(10 * sizeof(int)); A[2] = 10; printf("%d", A[2]); should give you 10 – codey modey Feb 15 '14 at 09:02
  • pointers do behave **like** arrays in certain cases, however pointers and arrays are not the same. Use `sizeof` operator and you'll see the difference. – ajay Feb 15 '14 at 09:04
  • Will the pointer way be called as a struct hack..as it is doing the same thing only the pointer is storing memory in heap..away from struct memory.. – codey modey Feb 15 '14 at 09:48
  • No, it's not a hack. And pointer is not **storing** memory. It's containing the address of the memory allocated on the heap. What do you understand by a hack anyway? – ajay Feb 15 '14 at 09:56
  • After reading all the answers, I conclude struct hack means to make a contiguous variable size memory for a struct. As a memory allocated for normal struct is contiguous in general, so keeping a 0 length array in the end of struct helps us to extend the memory contiguously to a custom size at the runtime. While keeping a pointer means keeping address of some memory not in continuation with the struct memory address. – codey modey Feb 15 '14 at 18:35
  • Yes, right but please don't call it a hack. It's explicitly mentioned in the C standard and a common practice to use as per the need and suitability. Hack means you are doing something **clever** or there's a **gotcha** involved. – ajay Feb 15 '14 at 19:05
2

You have lp as an auto variable so you can't assign a new address to it, lp itself is not a pointer. Correct initialization looks like:

struct tode lp = { .p = malloc(sizeof(int[10])) };

And this is no struct hack, just an ordinary pointer member. Since you deal with the address anyhow, there is no way to have this consecutive.

BTW: struct hack is not a "hack", it is part of the C standard (sine C99) and called "flexible array member".

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Why isn't this a struct hack(flexible data structure)..your pointer can behave as an array? I didn't get what do you mean when you say you deal with the address any how. – codey modey Feb 15 '14 at 08:52
  • 1
    @codeymodey When you make a pointer point to the first array member, you can access the elements in the same way as you would using the array name. This doesn't mean the pointer and the array are the same. They have different pointer arithmetic and different sizeof values. Pointers and arrays are **different** types. – ajay Feb 15 '14 at 09:26
  • 2
    @codeymodey, pointer and array are different beasts, you should read up on that. A pointer holds the address of data that is stored somewhere else. An array has the data in place, in the case of a flexible array member directly attached to the initial part of the `struct`. – Jens Gustedt Feb 15 '14 at 09:26
  • exactly, thanks for reminding me the difference it just skipped my head. What is the harm in keeping the data somewhere else(i.e. heap), why do you need to keep the data aligned with the struct memory? – codey modey Feb 15 '14 at 09:39
  • If you keep data somewhere else, then you should remember to `free` it separately after you are done with it. If you keep the data aligned with the structure, then when you free the structure, you free the whole memory your structure was holding. – ajay Feb 15 '14 at 10:03
  • @codeymodey Please see my answer. I have updated it for your above question. – ajay Feb 15 '14 at 10:11
0

If you want to make variable buffer struct use array as last element rather than pointer. Or assign the address of the pointer variable appropriately, so that it points to allocated memory.

For example, with your code p will have any random value. But you expect it to be lp + sizeof(lp->g) (leaving assign any padding issues for simplicity).

So rather you should define it as, and extend p as as you want.

struct tode{
      int g;
      int p[1];
  };
Rohan
  • 52,392
  • 12
  • 90
  • 87