I understand your use here, but I would use memset() to clear all the memory added to the end of B->array before returning.
I run with -std=C18, but do declare all my locals at the top of the scope. Personal preference!
I understand your use here, but I would use memset() to clear all the memory added to the end of B->array before returning.
I run with -std=C18, but do declare all my locals at the top of the scope. Personal preference!
>There ARE legitimate reasons to avoid initialising all variables, namely speed, that initialisation takes an instruction
Yet here you are, scrambling to figure out where you screwed up.
The imaginary microsecond you saved has turned into very real days of wtf is wrong with my code.
Your first order of business is to make the damn thing work by the most reliable means possible.
One of those things is rigorous initialisation of variables.
When it works, then you profile and optimise.
> yes it means the occasional bug creeps in when I forget it's not initialised but it's not often that happens.
ROFLMAO
How many times do you plan falling down the same hole, wondering how to get yourself out of it, before you realise a better way might be to avoid falling down the hole in the first place?
If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
If at first you don't succeed, try writing your phone number on the exam paper.
By clearing the memory you miss the point of the function, the point of the function is ONLY to allocate the TOTAL elements, if different set of functions should manage count, like this:
Whether you're taking more members or less, this function would be a sub call of growth/shrinkage functions, since your code will always need the count of used elements for interacting with the array you would call the count management functions & not directly the total elements management functions, those only get involved when you have a rough idea of how big the count will likely get but don't have an exact amount, an example would be the printfv family of functions, when I did my own versions (mainly out of the need to process the string sizes) I would do a pre-analysis of the arguments, identifying every instance of the % character and just adding sizeof(uintmax_t) * CHAR_BIT to the total I would allocate, then I would proceed to grow the count for each argument as I actually process it, it saved time overall by reducing the number of times I needed to realloc the array.Code:void* need_members( struct buffer *B, uint count ) { size_t start; uchar *array = (B->total < count) ? grow_buffer( B, count ) : B->array; if ( !array ) return NULL; start = (B->count < count) ? B->count : count; memset( array + B->Tsize * start, 0, B->Tsize * (B->total - start) ); return array; }
Since you bring god into it:
Your project works in "debug mode" (by this I assume you mean with no optimizations), but not in "release mode" (optimizations turned on). If that is the case it is far more likely that the optimizer are making use of an assumption you made in your code.There is an old joke, about a person who dies in a flood, after telling all their potential rescuers that "Gold will provide".
In heaven they ask "God, why did you forsake me, and leave me to die"?
And God says "What! I sent you a car, a truck, a boat and a helicopter, what more could I do?".
Does it compile cleanly with ALL compiler warnings tuned on?
You can still debug with optimizations turned on - it can just be a little more confusing, as the code is all jumbled up.
If you can point me to how I can recreate this bug with minimal effort on my part (e.g. not much more than "git clone ..." and then type "make mybug") I can take a look at it.
If you can't stop talking to me like I'm a 2 year old who doesn't know the 1st thing about programming then please just stop talking, what you're doing is technically bullying, just because I have a high tolerance doesn't mean my patience will last forever, of course I have all warnings active, rather they're set to be errors with -Werror, I don't tolerate seeing any compile time problems, errors or otherwise, as for that quote I already know it, did you really think I would blame god for my own mistakes? Of course not, I choose this method because I prefer it, sure I sometimes have to stress out over bugs but that's GOOD thing, it's unhealthy to have no stress in your life, it just seems like you have such a stressful life that you can't see that, in which case that's a bad thing, all things in moderation after all.
As for re-creating the bug, it really is quite simple just clone the code & run a few times until you see the bug occur, it's not every time after all. As for a link to the code, I'm in the middle of re-vamping my makefiles to be simpler to configure so I'll upload again after I'm finished (which might take the weekend as I'm lazy)
I don't get where you are trying to reach... this:
Won't allocate the array b _inplace_... This is only a syntatically easy way to declare an object anywhere you want, but is equivalent to declaring both a and b in the beginning of the code... We are talking about C here, don't we?Code:int f( ... ) { int a[10]; int n; n = g( a ); if ( ! n ) return 0; int b[10]; n = h( a ); return n; }
And in ANY code showed so far the objects are allocated in .bss (local objects aren't, unless they are static).
And, NO... this:
Here b is initialized at RUNTIME because it is on stack OR in a register...Code:int f( ... ) { int b = 0; ... }
The compiler can discard this object only if the optimizations are ON and if the object is **invariant**.
AND... local objects aren't initialized to zero by default... Only static local objects OR global objects... This:
Isn't initialized... This:Code:int a[10]; // local
Will be, because ONE item is initialized, ALL itens are... to zero...Code:int a[10] = { 0 }; // local
And, in both cases the object will be placed ON STACK, not at .bss.
No dynamic allocations occur there (unless you count stack allocations, I'm not), also you should not use 2 different functions for allocations, let's go with the examples I gave for more clarity:
Notice how I discard the pointer returned by grow_buffer() when I call it directly, that's because at that point the count of the buffer is still at it's starting point, to use the pointer before calling more_members() would instead corrupt the buffer because the data you're expecting to be in one place when you added before the increase in count will just get overwritten when the correct code uses the old count as the initial index to write characters at, likewise you shouldn't edit the count directly, instead leave it to functions that MAY call grow_buffer(), calling grow_buffer() directly is only for optimisation of allocations, never should it be use for the array count outside of functions like more_members() which are easier to bug fixCode:int foo( char const *args, ... ) { ... if ( !grow_buffer( &B, total ) ) return -1; do { ... val = va_arg( va, int ) itoa( tmp_text, val ); ... count = B.count; array = more_members( &B, count + strlen(tmp_text) ); if ( !array ) break; strcat( array + count, tmp_text ); ... } while ( *args ); printf( "%s\n", B.array ); free_buffer( &B ); va_end( va ); return 0; }
I know it's initialised at runtime, that's precisely why I don't initialise them until I need them, the reason is simple, if the function can often exit before needing the variable then the wasted time on initialising the variable adds up, if the function is called enough times the wasted time will be noticed by the user, especially on older machines, yes it can be frustrating to find variables that you forgot to initialise prior to using it but I find that I tend to learn more about my code &/or the APIs I'm using in the process, that inevitably leads to better code overall.