1

I need to allocate memory to an array inside my struct, this array has no defined size at the beginning when i define the struct:

typedef struct stacks {
    int size; // Stores the size of my -values- array
    int sp; //points to the top of the stack, my stackpointer
    int *values;
} STACKS;

So, to initialize my struct i wrote this function, that allocates (using calloc?) memory to my array, and i put inside SIZE variable, the new size of my array .

#define MAXIMUM 10

int initStacks(STACKS *s){
    s->values = calloc(MAXIMUM,sizeof(int));
    s->size = MAXIMUM;
    s->sp = 0;
    return 0;
}

Now, if i want to push something to the top of the stack (LIFO) i use this:

int pushs(STACKS *s, int x){
    if (s->sp==s->size) {
        realloc(s->values, MAXIMUM * sizeof(int));
        s->size*=2;
    }

    s->values[s->sp]=x;
    s->sp++;
}

Is this the correct way of doing this?

Is realloc working as it should in my function?

Thank you very much for your help!

EDIT:

would this make more sense? This way, i really don't need to declare the value of the array, being that defined with #define maximum 10

typedef struct stacks {
    int size; // guarda o tamanho do array valores
    int sp;
    int *values;
} STACKS;


int initStacks(STACKS *s){
    s->values = calloc(1,sizeof(int));
    s->size = 1;
    s->sp = 0;
    return 0;
}

int isEmptys(STACKS *s){
    return((s->sp)==0);
}

int pushs(STACKS *s, int x){
    s->size++;
    realloc(s->values, s->size * sizeof(int));
    s->values[s->sp]=x;
    s->sp++;
}
skills
  • 315
  • 5
  • 17
  • 1
    You should at least check the return value of realloc, if it is NULL the request has failed and you must re(al)locate the stack. Also the s->size==MAXIMUM is a bit dubious comparision, perhaps it should be s->sp==MAXIMUM – Tero Tolonen May 10 '15 at 19:20
  • Man, of course that comparison is stupid! Thank you for pointing it out! Maybe better would be `s->sp==s->size` ? Meaning that the stackpointer got to the point where is equal to the maximum? – skills May 10 '15 at 19:21
  • 1
    You showed the `initStacks` function twice. – Keith Thompson May 10 '15 at 19:28
  • @KeithThompson you are right, sorry. Was trying to write the question carefully and ended up by adding the same function twice. – skills May 10 '15 at 19:30
  • You should use size_t for the indexes. Also, I would not allocate any memory in init, but have push test for both: values == NULL (initial value) and sp < size. also note that calloc is not necessary, as you don't use it's features anyway when extending with realloc. – too honest for this site May 10 '15 at 20:13
  • @Olaf are you commenting on the code bellow EDIT ? – skills May 10 '15 at 20:15
  • You should definitely try to *MINIMIZE* the #/times you call realloc(). If you must realloc at all, I would recommend: `realloc(s->values, s->size * 2 * sizeof(int));` (double the space each time). ALSO: be sure to check for error return! – FoggyDay May 10 '15 at 20:17
  • @skills: Yes , I do :-). – too honest for this site May 10 '15 at 20:17
  • 2
    As @FoggyDay wrote, I would not realloc for every single element, but e.g. double the size each time. So you loose not more than 50% memory. Alternatively, you could use a completely different structure: a linked list with each node holding some elements. This avoids realloc completely, including its pitfalls (fragmentation, out of memory). Just in case you a planning on something bigger;-) – too honest for this site May 10 '15 at 20:21
  • Or alternatively, you could just expand the stack by a fixed number of elements, a constant growth expansion (ex: make space for 20 more elements) as opposed to doubling factor. On that you would have more allocs than a doubler, but still be be considerably better than a grow-per-push algorithm depending on the size of the rate chosen. – WhozCraig May 10 '15 at 23:43
  • @WhozCraig: Right: realloc for every push would be not good. Wether you double, tripple or just increase by a fixed amount depends on what you expect (and how much entries you are going to process. That's why I introduced the linked list, or a combination: list of stacks: If a stack if full, a new is appended to the list and used. Actually a doubly-linked list would be required for the pop operation and to release each sub-stack once not required. This completely avoids the potentially very costly realloc (it might copy the array!). – too honest for this site May 10 '15 at 23:52

2 Answers2

3

Assuming you have an original size factor (the name capacity would be as-appropriate, if not more so), your original code lacks several things:

  • Compares the size against a constant, rather than the current sp against the stack current size.
  • Does not save nor test the return result of realloc
  • Does not actually double the allocation (you're missing the 2x in the realloc expression.
  • Declares an int return result, but no such return exists.
  • Has no way of communicating back to the caller the push result (success or not). That missing return result would be ideal for this, btw.

Addressing all of these:

int pushs(STACKS *s, int x)
{
    if (s->sp == s->size) 
    {
        void *pv = realloc(s->values, 2 * s->size * sizeof *(s->values));
        if (pv != NULL)
        {
            s->values = pv;
            s->size *= 2;
        }
        else
        {
            fprintf(stderr, "Failed to resize stack\n");
            return -1;
        }
    }

    s->values[s->sp++] = x;
    return 0;
}

Untested, but hopefully close enough.

Best of luck

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • Thank you very much @WhozCraig , in fact, Telo warned me about the non-sense comparison. I will convert my code using your insights and will return with a verdict :) – skills May 10 '15 at 19:49
  • One thing, would it make sense to not have MAXIMUM and keep re-allocating, by adding one unit of (int) to the array? – skills May 10 '15 at 19:57
  • @Olaf thanks! One question, how do i free() now? Because i don't want to free the all memory that was allocated, when removing one element of the array! – skills May 10 '15 at 20:42
  • @skills: A stack is a data strcture which does only define push and pop operations (I think you are already aware of this). If you think about shrinking the stack, the general answer for your choosen structure is not to. Just leave the memory as-is and `free()` once you do not need the complete stack anymore. If this is a problem, you most likely will be happier with a different approach like the linked list I mentioned in another comment. However, you might realloc the reverse when popping from the stack: `sp < size/2` or so. I would hold a min size, though for efficiency reasons, – too honest for this site May 10 '15 at 20:57
  • Craig, if the idea behind `return -1` and `return 0` is to return a boolean result, it would be better to use positive logic: return true on success. Also, return 1, not -1 as this is the correct integer value of boolean "true". Even better: declare the function `bool` from the start and avoid ints completely. – too honest for this site May 10 '15 at 21:16
  • @Olaf it wasn't the idea to return a boolean result, just like so many other unistd apis returning `int` (the pthread apis, the socket apis, etc), that have zero on success, non-zero on failure. But honestly the OP is free to do what they want regardless so long as the function returns *something* and doesn't leave that up to UB (which the OP post did). And that `sizeof` is correct, defined behavior, and exactly what I intended. – WhozCraig May 10 '15 at 22:50
  • Anyway, I would return 1 then I actually don't care much about inverted results. Don't forget the stdlib was mostly designed when there was nothing like bool in C at sight. Regarding the sizeof: sorry, I forgot it is actually a prefix unary operator. I use it myself like a function, that's what confused me and just triggered on the * before the parenthesis. Expecially as they are not required (I had expected `sizeof(*...). – too honest for this site May 10 '15 at 23:20
  • @Olaf Yeah, I could have just used `sizeof *s->values`, but someone somewhere may bite into that apple on a double-indirection pointer somewhere and think it will work (which it sort-of-will, just not as they think). I can see now how it may look a little strange at first glance though, certainly. – WhozCraig May 10 '15 at 23:39
  • I used that construct myself for years, but somehow I got used to the un-parenthised variant. OTOH I use parens with sizeof. Would say we are en par:-) – too honest for this site May 10 '15 at 23:44
-1

Although not directly an answer to the actual question, but more to the general problem, I post this as it does not fit into a comment.

If you expect excessive push/pop operations and memory usage, the following might be an alternative:

typedef struct SubStack_s {
    struct SubStack_s *prev;
    int data[ENTRIES_PER_SEGMENT];
} SubStack;

typedef struct {
    SubStack *tos;    // init to NULL
    size_t sp;        // init to 0
} Stack;

The basic idea is to push elements onto each substack until full (as you already do). If the current one is full, you alloc a new one, chain them (new->prev = old) and continue with the new one (storing new to Stack.tos)

Pop works similar, free'ing each substack once it is not used anymore.

That concept is called "fragmented stack". It is much more efficient than the realloc-approach (it avoids copying) and does not fragment RAM as all block are of equal size. Oh, and it allows to have pointers into the stack, which the realloc-varaint does not, because the address of the stack can change.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • Are you aware of the techniques OSes apply in order to avoid copying when `realloc` is called? – autistic May 30 '15 at 04:34
  • @undefinedbehaviour: No, I started working in this job just several decades agot and still don't know the basics (note: sarcasm - just to make sure!). Are you aware of how costly (clocks) an OS call and page-table manipulations actually are compared to a simple list-append (expecially for single-threaded code)? – too honest for this site May 30 '15 at 14:34
  • Okay, I'll bite... and how do you intend to avoid allocation (and thus the OS calls) by using linked lists? A lot has changed in the last *several decades*, and it would be an error to assert some kind of superiority based on age if you haven't been keeping up-to-date, so please... just leave ancient history out of this discussion. – autistic May 30 '15 at 14:37
  • @undefinedbehaviour: I could hav added that I livend on an island without internet and just sticking to the old practices back then. Note that for malloc(), that is - as much as realloc() a library call in the first place. But malloc() can (and does) manage an internal free list, while ralloc would have quite some problems to do so (even more as both use the same fre-list and heap of course), as it had to anticipate which block will require to be extended. Argumentum ad antiquitatem is certainly not what I do. Please work on your sarcasm interpreter. ... – too honest for this site May 30 '15 at 14:46
  • Wouldn't the very presence of `realloc` suggest that standard library and OS developers might program that heap data structure to expect allocations to *grow*? What problems does `realloc` face currently in managing this *internal free list*? Do you know how todays OSes typically manage memory fragmentation? – autistic May 30 '15 at 14:51
  • .... "There are two kinds of idiots: one, hwo says: "This is old it is good" The other who stated "This is new, this is better". This has always been one of my guides. Just think about the implications! Note, also, The theories behind modern techniques are often decades old already. Lot of these techniques, hwoever, have not been used back then, as they were too complex or required ressorces not available until now (or even in our future). – too honest for this site May 30 '15 at 14:52
  • Yes. Virtual memory has existed for decades. Paging (part of which is the process of moving data from primary to secondary memory, and back) has existed for decades... These aren't new technologies. Do you know how they're typically used to pre-emptively avoid unnecessary copying and memory fragmentation when using `realloc` aggressively? – autistic May 30 '15 at 14:56
  • @undefinedbehaviour: I give up. Neither do I have the time, nor the will to continue this discussion. Sticking a finger in the wound (detecting weak arguments) is one thing, not willing to understand and lack of basic concepts is another. This discussion is over for now, this is not the place and you obviously lacking the will to thing deeper. – too honest for this site May 30 '15 at 14:58
  • @undefinedbehaviour: It's not me to prove, but you to prove me wrong, as you are commenting on my answer. I do have a life outside SO. I'm done. – too honest for this site May 30 '15 at 14:59
  • So you're familiar with Latin? That should be *argumenta*; you're referring to it in a plural form. Are you also familiar with the term "*onus probandi*"? It is my job to prove what I'm claiming, which is that [`realloc` is an abstract concept which doesn't necessarily introduce more *copying*](http://www.iso-9899.info/n1570.html#7.22.3.5). Wouldn't it be fair to say the onus is equally on me to prove what I claim as it is on you to prove what you claim? You've formed a concrete definition for `realloc` which *does* introduce more copying... Can *you* prove that it's the only one? – autistic May 30 '15 at 15:16