Get Screwed with C programming PDF Print E-mail

  • Non-terminated comment, "accidentally" terminated bysomesubsequent comment, with the code in between swallowed.

  •         a=b; /* this is a bug


            c=d; /* c=d will never happen */

  • Accidental assignment/Accidental Booleans
  •         if(a=b) c;      /* a always equals b, but c will be executed if b!=0 */
    Depending on your viewpoint, the bug in the language is that theassignment operator is too easy to confuse with the equality operator;or maybe the bug is that C doesn't much care what constitutes a booleanexpression: (a=b) is not a boolean expression! (but C doesn'tcare).
    Closely related to this lack of rigor in booleans, consider thisconstruction:
            if( 0 < a < 5) c;      /* this "boolean" is always true! */
    Always true because (0

    Or consider this:

            if( a =! b)c;      /* this is compiled as (a = !b), anassignment, rather than (a != b) or (a == !b) */

  • Unhygienic macros

  •         #define assign(a,b) a=(char)b


            assign(x,y>>8)

    becomes
                   x=(char)y>>8   /*probablynot what you want */ 
     
  • Mismatched header files

  • Suppose foo.h contains:

            struct foo { BOOL a};





      file F1.c  contains


            #define BOOL char


            #include "foo.h"





      file F2.c contains 


            #define BOOL int


            #include "foo.h"

    now, F1. and F2 disagree about the fundamental attributes of structure"foo". If they talk to each other, You Lose! 
     
  • Phantom returned values

  • Suppose you write this

        int foo (a)


        { if (a) return(1); } /* buggy, because sometimes no value is returned  */

    Generally speaking, C compilers, and C runtimes either can't or don'ttell you there is anything wrong. What actually happens depends on theparticular C compiler and what trash happened to be left lying aroundwherever the caller is going to look for the returned value. Dependingon how unlucky you are, the program may even appear to work for awhile.

    Now, imagine the havoc that can ensue if "foo" was thought toreturn a pointer! 
     

  • Unpredictable struct construction

  • Consider this bit packing struct:

        struct eeh_type


        {


                uint16 size:          10;   /* 10 bits */


                uint16 code:           6;   /* 6 bits */


        };

    Depending on which C compiler, and which "endian" flavor of machine youare on, this might actually be implemented as
            <10-bits><6-bits>
    or as

            <6-bits><10-bits>





    Also, again depending on the C compiler, machine architecture, and various mysterious preference settings,


    the items might be aligned to the nearest 8, 16, 32, or 64 bits.





    So what matters? If you are trying to match bits with a real world file,


    everything!








    Need another way to lose big? How about this:





    Rect foo = {0,1,2,3}; // assign numbers to the first four slots





    You may think you know what those four slots are, but there's at least an


    even chance you'll have to discover the hard way if the structure ever


    changes.


  • Indefinite order of evaluation (contributed by Xavier @triple-i.com)
  •         foo(pointer->member, pointer = &buffer[0]);
    Works with gcc (and other compilers I used until I tried acc) and doesnot with acc. The reason is that gcc evaluates function arguments fromleft to right, while acc evaluates arguments from right to left.
    K&R and ANSI/ISO C specifications do not define the order ofevaluation for function arguments. It can be left-to-right,right-to-left or anything else and is "unspecified". Thus any codewhich relies on this order of evaluation is doomed to be non portable,even across compilers on the same platform.

    This isn't an entirely non controversial point of view. 
     

  • Easily changed block scope (Suggested by Marcel van derPeijl )

  •     if( ... ) 


            foo(); 


        else 


            bar();

    which, when adding debugging statements, becomes

        if( ... ) 


            foo();          /* the importance of this semicolon can't be overstated */


        else 


            printf( "Calling bar()" );      /* oops! the else stops here */


            bar();                          /* oops! bar is always executed */

    There is a large class of similar errors, involving misplacedsemicolons and brackets. 
     
  • Permissive compilation (suggested by James M.Stern)

  • I once modified some code that called a function via a macro:
            CALLIT(functionName,(arg1,arg2,arg3));
    CALLIT did more than just call the function. I didn't want to do theextra stuff so I removed the macro invocation, yielding:
            functionName,(arg1,arg2,arg3);
    Oops. This does not call the function. It's a comma expression that:
    1. Evaluates and then discards the address of functionName
    2. Evaluates the parenthesized comma expression (arg1,arg2,arg3)
    C's motto: who cares what it means? I just compile it!My own favorite in this vein is this:

            switch (a) {


            int var = 1;    /* This initialization typically does not happen. */


                            /* The compiler doesn't complain, but it sure screws things up! */


            case A: ...


            case B: ...


            }

    Still not convinced? Try this one (suggested by Mark Scarbrough ):

    #define DEVICE_COUNT 4 


    uint8 *szDevNames[DEVICE_COUNT] = {


            "SelectSet 5000",


            "SelectSet 7000"}; /* table has two entries of junk */

  • Unsafe returned values (suggested by Bill Davis This e-mail address is being protected from spambots. You need JavaScript enabled to view it >) 

  • char *f() { 
       char result[80]; 
       sprintf(result,"anything will do"); 
       return(result);    /* Oops! resultis allocated on the stack. */ 
     }


    int g() 
       char *p; 
       p = f(); 
       printf("f() returns: %s\n",p); 
    The "wonderful" thing about this bug is that it sometimes seems to be acorrect program; As long as nothing has reused the particular piece ofstack occupied by result
     

  • Undefined order of side effects. (suggested by michael This e-mail address is being protected from spambots. You need JavaScript enabled to view it .EDUandothers) 
  • Even within a single expression, even with only strictly manifestside effects, C doesn't define the order of the side effects.Therefore, depending on your compiler, I/++I might be either 0 or 1.Try this:

    #include 





    int foo(int n) {printf("Foo got %d\n", n); return(0);}





    int bar(int n) {printf("Bar got %d\n", n); return(0);}





    int main(int argc, char *argv[]) 


    {


      int m = 0;


      int (*(fun_array[3]))();





      int i = 1;


      int ii = i/++i;





      printf("\ni/++i = %d, ",ii);





      fun_array[1] = foo; fun_array[2] = bar;





      (fun_array[++m])(++m);        


    }





    Prints either i/++i = 1 or i/++i=0;

    Prints either "Foo got 2", or "Bar got 2" 
     
  • Uninitialized local variables 
  • Actually, this bug is so well known, it didn't even make the list!That doesn't make it less deadly when it strikes. Consider the simplestcase:

    void foo(a)


    { int b;


      if(b) {/* bug! b is not initialized! */ }


    }

    and in truth, modern compilers will usually flag an error as blatant asthe above. However, you just have to be a little more clever tooutsmartthe compiler. Consider:

    void foo(int a) 


    { BYTE *B;


       if(a) B=Malloc(a);


              if(B) { /* BUG! B may or may not be initialized */ *b=a; } 


    }

  • Cluttered compile time environment 
  • The compile-time environment of a typical compilation is clutteredwith hundreds (or thousands!) of things that you typically have littleor no awareness of.  These things sometimes have dangerouslycommon names, leading to accidents that can be virtually impossible tospot.

    #include
    #define BUFFSIZE 2048 
    longfoo[BUFSIZ];               //notespellingofBUFSIZ != BUFFSIZE


    This compiles without error, but will fail in predictably awfuland mysterious ways, because BUFSIZ is a symbol defined bystdio.h.  A typo/braino like this can be virtually impossible tofind if the distance between the the #define and the error is greaterthan in this trivial example. 
     

  • Under constrained fundamental types

  • I've been seriously burned because different compilers, or evendifferent options of the same compiler, define the fundamental type int aseither 16 or 32 bits..  In the same vein, name any other languagein which boolean might be defined or undefined, or might bedefined by a compiler option, a runtime pragma (yes! we havebooleans!), or just about any way the user decided would work ok. 
     
  • Utterly unsafe arrays 
  • This is so obvious it didn't even make the list for the first 5years, but C's arrays and associated memory management are completely,utterly unsafe, and even obvious cases of error are not detected.

     int thisIsNuts[4]; int i; 
      for ( i = 0; i < 10; ++i ) 
      { 
        thisIsNuts[ i ] = 0;     /*Isn't it great ?  I can use elements 1-10 of a 4 element array,andno one cares */ 
      }

    Of course, there are infinitely many ways to do things like thisin C. 


    Read more: http://technicalstudies.youngester.com/2012/01/get-screwed-with-c-programming.html

     

    Your Comments Here


    FORM_CAPTCHA
    Refresh