Thread: Can someone remind me how to tell gcc not to 0 initialise everything in debug mode

  1. #16
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    950
    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!

  2. #17
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,126
    >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.

  3. #18
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by rstanley View Post
    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!
    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:
    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;
    }
    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.

  4. #19
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by Salem View Post
    >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?
    Say what you will, I don't mind the time lost, I have an eternity in heaven to look forward to, this life is just a pastime until God declares it time to take me.

  5. #20
    Registered User
    Join Date
    Sep 2020
    Posts
    339
    Quote Originally Posted by awsdert View Post
    Say what you will, I don't mind the time lost, I have an eternity in heaven to look forward to, this life is just a pastime until God declares it time to take me.
    Since you bring god into it:
    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?".
    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.

    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.

  6. #21
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by hamster_nz View Post
    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.

    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)

  7. #22
    Registered User
    Join Date
    Oct 2019
    Posts
    72
    Quote Originally Posted by awsdert View Post
    I mean for reasons like this:
    Code:
    void* grow_buffer( struct buffer *B, uint total )
    {
        void *array;
        if ( B->total >= total )
            return B->array;
        array = realloc( B->array, B->Tsize * total );
        if ( !array )
            return NULL;
        B->array = array;
        B->total = total;
        return array;
    }
    Notice how I don't need the array variable until after I checked I'm going to be using it, in C99 I could just declare it later but in C89 it must be at the top of the scope, since I like to keep my code as C89 compatabile as possible I have to decide what scopes I need to declare my variables in, I also don't like going to far in with the scope like say this:
    Code:
    void* grow_buffer( struct buffer *B, uint total )
    {
        if ( B->total < total )
        {
            void *array = realloc( B->array, B->Tsize * total );
            if ( !array )
                return NULL;
            B->array = array;
            B->total = total;
        }
        return B->array;
    }
    If there's decent reason to use a sub scope then I avoid it & stick to the upper scope
    Just a nitpick here. It looks like your code will definitely leak memory

  8. #23
    Registered User
    Join Date
    Oct 2019
    Posts
    72
    Quote Originally Posted by ghoul View Post
    Just a nitpick here. It looks like your code will definitely leak memory
    Well, on taking a second look, it looks like you are right.


    If the area pointed to was moved, a free(ptr) is done.

  9. #24
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by ghoul View Post
    Just a nitpick here. It looks like your code will definitely leak memory
    Not seeing where the leak would occur, mind cluing me in?

  10. #25
    Registered User
    Join Date
    Feb 2019
    Posts
    963
    I don't get where you are trying to reach... this:
    Code:
    int f( ... )
    {
      int a[10];
      int n;
    
      n = g( a );
      if ( ! n )
        return 0;
    
      int b[10];
      n = h( a );
    
      return n;
    }
    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?

    And in ANY code showed so far the objects are allocated in .bss (local objects aren't, unless they are static).

  11. #26
    Registered User
    Join Date
    Feb 2019
    Posts
    963
    And, NO... this:
    Code:
    int f( ... )
    {
      int b = 0;
      ...
    }
    Here b is initialized at RUNTIME because it is on stack OR in a register...
    The compiler can discard this object only if the optimizations are ON and if the object is **invariant**.

  12. #27
    Registered User
    Join Date
    Feb 2019
    Posts
    963
    AND... local objects aren't initialized to zero by default... Only static local objects OR global objects... This:
    Code:
      int a[10];  // local
    Isn't initialized... This:
    Code:
      int a[10] = { 0 };  // local
    Will be, because ONE item is initialized, ALL itens are... to zero...
    And, in both cases the object will be placed ON STACK, not at .bss.

  13. #28
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by flp1969 View Post
    I don't get where you are trying to reach... this:
    Code:
    int f( ... )
    {
      int a[10];
      int n;
    
      n = g( a );
      if ( ! n )
        return 0;
    
      int b[10];
      n = h( a );
    
      return n;
    }
    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?

    And in ANY code showed so far the objects are allocated in .bss (local objects aren't, unless they are static).
    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:

    Code:
    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;
    }
    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 fix

  14. #29
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by flp1969 View Post
    And, NO... this:
    Code:
    int f( ... )
    {
      int b = 0;
      ...
    }
    Here b is initialized at RUNTIME because it is on stack OR in a register...
    The compiler can discard this object only if the optimizations are ON and if the object is **invariant**.
    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.

  15. #30
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,582
    Quote Originally Posted by flp1969 View Post
    AND... local objects aren't initialized to zero by default... Only static local objects OR global objects... This:
    Code:
      int a[10];  // local
    Isn't initialized... This:
    Code:
      int a[10] = { 0 };  // local
    Will be, because ONE item is initialized, ALL itens are... to zero...
    And, in both cases the object will be placed ON STACK, not at .bss.
    Not always the case, case & point how debug & release executables can get different behaviour on the same code

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 1
    Last Post: 04-24-2010, 01:35 PM
  2. Using the Debug mode ((F10) V.S 2005
    By csonx_p in forum Windows Programming
    Replies: 21
    Last Post: 06-17-2008, 05:06 PM
  3. Debug Mode
    By Coding in forum C# Programming
    Replies: 5
    Last Post: 02-14-2008, 03:00 PM
  4. Replies: 4
    Last Post: 09-16-2006, 07:11 PM
  5. Debug Mode Vs. Release Mode
    By incognito in forum Tech Board
    Replies: 5
    Last Post: 12-18-2003, 04:06 PM

Tags for this Thread