3

I'm pretty new to c, so if my steps are wrong, please let me know. Let's say that I have something like the following:

struct graphNode{
    int val;
    graphNode* parent;
    int succSize;
    int succMaxSize;
    graphNode* succ[1];
};

I will create a new node with:

graphNode *n;
n = malloc(sizeof(struct graphNode));
assert(n);
n->val = 1;
n->parent = NULL;
n->succSize = 0;
n->succMaxSize = 1;

Then, if I want to add a successor to the node

if (n->succSize == n->succMaxSize){
    n->succ = realloc(n->succ, sizeof(graphNode*) * n->succMaxSize * 2);
    n->succMaxSize *= 2;
} 
n->succ[succSize] = n2; //n2 is of type graphNode*
succSize++;

Is this correct? Do I need to realloc for the struct as well or is realloc of the array enough? Do I need to malloc for the initial array? Should the initial array size be included in my malloc call for n?

Lunyx
  • 3,164
  • 6
  • 30
  • 46
  • your initial structure doesn't need realloc. What do you want to achieve ? I feel like it's not elegant to realoc. – johan d Oct 17 '13 at 22:00
  • Why not use C++ and vector this problem out of ones life? – Ed Heal Oct 17 '13 at 22:05
  • 1
    @EdHeal Why not use a Python tuple or a JavaScript array to avoid memory management altogether? –  Oct 17 '13 at 22:08
  • @H2CO3 - I was summing that the poster requires native code and that C++ may be a good stepping stone – Ed Heal Oct 17 '13 at 22:13
  • @EdHeal Well, maybe, yes. But what if he can't use C++ for some reason? –  Oct 17 '13 at 22:14
  • @H2CO3 - Just a comment and possibility an avenue to explore. Besides the concept of a vector can be implemented in C and might be interesting reading for the poster – Ed Heal Oct 17 '13 at 22:17

5 Answers5

7

The usual way to define a "stretchy" array member in C is to either specify a size of 0 or no size at all, e.g.:

struct foo {
    int stuff;
    bar theBars[]; // or theBars[0]
};

With this definition, sizeof(struct foo) will include all the elements other than the array at the end, and you can allocate the right size by saying malloc(sizeof(struct foo) + numberOfBars * sizeof(bar)).

If you need to reallocate it to change the number of bar elements, then you'll use the same formula (but with a new numberOfBars).

To be clear, you can't just realloc part of a struct. You have to realloc the whole thing.

danfuzz
  • 4,253
  • 24
  • 34
  • Is having `bar theBars[];` the same as having `bar *theBars;`? Is one more preferable over the other? – Lunyx Oct 17 '13 at 22:30
  • @Daniel, no it is different. See my answer below for more explanation. – Charlie Burns Oct 17 '13 at 22:45
  • Tried to compile the code here and got `error: array type has incomplete element type`. Is there something fundamental that I'm missing? – Lunyx Oct 18 '13 at 20:38
  • Is it because I am using a `struct foo` and trying to use an array of `foo` inside the struct? Is this possible to achieve? – Lunyx Oct 18 '13 at 20:40
  • My code was just a sketch, meant to highlight the solution I was talking about. For example, I never defined the type `bar`. If you change `bar` to (say) `int` it should compile. – danfuzz Oct 18 '13 at 23:23
  • There is nothing wrong with including a *pointer* to a struct type within the definition of that struct type, but you can't include that type directly. (The latter would imply an infinite-size struct.) – danfuzz Oct 18 '13 at 23:26
1

realloc(ptr,size) needs 2 parameters, not 1 as used in realloc(sizeof(graphNode*) * n->succMaxSize * 2)

// Something like ...
graphNode *n;
n->succSize = 0;
n->succMaxSize = 0; // set to 0
n->succ = NULL;  // Initialize to NULL

// Then, if OP wants to add a successor to the node
if (n->succSize <= n->succMaxSize){
  n->succ = realloc(n->succ, sizeof(graphNode*) * n->succMaxSize * 2);
  n->succMaxSize *= 2;
} 
n->succ[succSize++] = n2;

As with all memory allocations, check for NULL return. In realloc(), one should save the original value, so if the realloc() fails, the original pointer is not lost.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    Thanks for reminding me on that. In general, would it be better to separate the `n->succ[succSize++]` into an assignment, then an increment, in order to make the code more obvious? – Lunyx Oct 17 '13 at 22:50
  • 1
    This is a good answer for when succ is defined as graphNode*. – Charlie Burns Oct 17 '13 at 22:55
  • @Daniel Concerning separate or included `succSize++`: That is [holy war](http://meta.programmers.stackexchange.com/questions/2638/what-is-a-point-of-the-holy-war-tag) ground. 6.001 or half-a-dozen of the other. – chux - Reinstate Monica Oct 17 '13 at 23:28
  • Another thing is you said that we should save the original value so that we still have the original pointer if realloc fails. However, wouldn't realloc fail only when there's no memory available to be allocated? In what cases would we actually want to keep running the program instead of exiting with a out of memory error? – Lunyx Oct 18 '13 at 00:14
  • @Daniel Sounds like a good SO question. But I'd say a good program _gracefully_ exits as it is able (frees allocated resources: memory, FILE handles, locks, blocks, toys, Lego, etc.). Such a program does that already, so why not do that on exit? Further, say your code is not _the_ program, but a library. Would you want a library driving the train? – chux - Reinstate Monica Oct 18 '13 at 01:08
  • From what I understand, just closing a program is enough to clear up allocated memory. I'm not sure about file handles, but intuitively, I'd think that it should behave the same. However, the point you bring up about it being a library is definitely worth considering. – Lunyx Oct 18 '13 at 04:38
1

Usually when you see struct definition where the last field is an array of size 0 or 1 it means the author is going to do some subtle stuff with malloc when the struct is malloced.

For example

struct foo {
   int x;
   :
   : 
   type a[0];
};

With a malloc like

  struct foo *p = malloc(sizeof(*p) + (n * sizeof(type));

What this does is it allocates a contiguous chunk of memory for the struct and the trailing array. In this case the array size is n. So references to the array in this case are:

p->a[i] // where i >= 0 and i < n

One reason for doing this is to save memory.

I'm sure there are better explanations for this on StackOver; it's a very common C idiom.

It's generally not used when the array is dynamic. Rather, it is used when the array size is known at malloc() time. You can use dynamically, of course, but you have to realloc the entire memory chunk, not just the struct or array by itself. To increase the size to 2n you would say

  p = realloc(p, sizeof(*p) + (2 * n * sizeof(type)));

Now your array is twice is big as it was, and it's still one chunk of memory.

Charlie Burns
  • 6,994
  • 20
  • 29
  • Is it a requirement to have the array as the last field of the struct definition? >It's generally not used when the array is dynamic. What is "it" referring to here? When would you use `type a[0]` or `type a[]` over `type *a`? – Lunyx Oct 17 '13 at 22:55
  • I was trying to point out common C idiom when a person has a array of size 0 or 1 at the end of the struct. If you are the author of the struct definition and don't understand what I am saying you can ignore my answer. If someone else wrote it, my answer may be of interest to you. By "it" I mean "the idiom". One would use this technique when the number of successors if variable and you wanted to save some memory. – Charlie Burns Oct 17 '13 at 23:00
0

If you only want a single array, just make succ a single pointer and only use malloc/realloc etc. to allocate memory for the array.

graphNode* succ;

What you are doing is almost certain to break.

ralight
  • 11,033
  • 3
  • 49
  • 59
0

I too am new to C, but there's some things that I can see right off the bat. First of all, you can't re-allocate arrays. In c89, they're compile-time fixed-size. In C99 and C11, they can be dynamically allocated, but not reallocated (as far as I'm aware). So for this, you need to allocate a

graphnode *succ; 

pointer, and malloc(nodes * sizeof(node)).

graphNode* succ[1];

This creates an array of size one, not an array with a maximum index of one. So, it is the same (almost) functionally as

graphNode* succ;

except that you can't change its size once you've made it.

I think what you want is to make a tree, with a dynamically re-allocable amount of branches. In this case, you only need to reallocate the size of the graphNode* pointer, and then access each element via index as you would an array.

nitetrain8
  • 254
  • 3
  • 6
  • That makes sense. Just to be clear, you said that I should be reallocating the n->succ pointer, but I do I not need to realloc the struct pointer n? How does that work? Another person in an answer below said that I cannot realloc part of a struc and that I have to realloc the whole thing. – Lunyx Oct 17 '13 at 22:27
  • graphNode* succ[1]; is not "the same (almost)" as graphNode* succ; The first is an array of one pointer, the second is a pointer. Very different. See the accepted answer to see how this may be used. – Charlie Burns Oct 17 '13 at 22:50
  • @Daniel- this approach and the other are actually two different ones. In this case, the array of successors isn't part of the struct, only the pointer to the array is, and thus only the memory that the pointer points to needs to be reallocated. I wasn't aware until reading other answers of the "struct hack"- creating an empty array as the last element of a struct, and allocating that memory as part of the struct itself, so that the array memory is contiguous with the rest of the struct. In that case, you would have to reallocate the whole struct, since they are the same block of memory. – nitetrain8 Oct 17 '13 at 23:07