Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Is this undefined behavior in C?

  1. Aug 23, 2013 #1

    jim mcnamara

    User Avatar

    Staff: Mentor

    I really would appreciate seeing what opinions* there are on undefined behavior in C. Based
    on only the code snippets (A, B, C, D, E (yes, they have issues)) below and:

    From N1570 (C 11 standard draft)
    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

    This is what the standard says about Undefined Behavior, definitions:
    Code (Text):


    // Specifically, do these snippets result in UB:

    // A
    unsigned diff(unsigned a, unsigned b)
    {
       return a - b;
    }

    // B
    int *foo(void)
    {
       int *p=malloc(4);  
       if(p)
         *p=13;
       return p;
    }

    // C
    int foo(void)
    {
       foo_2();
       int x;
       return x/2;
    }

    // D
    int main(void)
    {
       int i=0;
       return ( i + 1 ) + ( i + 2);
    }

    //E
    int main(void)
    {
       int i=13;
       5/0;
       return i;
    }

     
    Can you defend your position? There are discussions like this elsewhere on the internet.
    * I used the word opinions above on purpose. I will explain later.
     
    Last edited: Aug 23, 2013
  2. jcsd
  3. Aug 23, 2013 #2

    CompuChip

    User Avatar
    Science Advisor
    Homework Helper

    First of all, all any example that does not compile does not exhibit undefined behavior, it is simply illegal. So your example E would not compile, as the compiler would issue a "dividing by 0" warning.

    I don't see any problem with example D - why would that give undefined behavior? It just returns 3.
    Example C does give undefined behavior, apart from any effects that foo_2() might have the variable x is not initialized.
    B again doesn't compile, because void* and int* are incompatible. If it were, I think it would be fine, except that you are leaking memory (but that's not undefined behavior).

    A is the one I'm not sure about, I guess if you would call diff(3, 9) you would get a very large number, which is completely predictable but not what you meant.
     
  4. Aug 23, 2013 #3

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    A. No. The difference between two unsigned ints is always well-defined, even in the case that a < b. Unsigned arithmetic is modulo N+1, where N is the largest possible value represented by that unsigned int. What you do get is implementation-defined behavior because the standard doesn't specify a value for N.

    B. Yes. You are assuming that sizeof(int)=4. There's no guarantee that this is the case.

    C. Yes. Using a variable (in this case, x) before it has been assigned a value is UB.

    D. No. Returning something other than 0, EXIT_SUCCESS, or EXIT_FAILURE is implementation-defined behavior, not undefined behavior.

    E. Yes. Just because you are dropping 5/0 on the bit floor doesn't excuse you from the fact that 5/0 is UB.
     
  5. Aug 23, 2013 #4

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    E compiles quite fine for me, multiple compilers. That pesky warning is just that, a warning. The compiler is being nice in detecting that UB at compile time. The nastiest thing about undefined behavior is that while it is illegal (by definition), the compiler can do *anything* in response to it and still be compliant with the standard.

    C != C++. void* is compatible with every pointer type in C. The recommended practice in many places is *not* to cast the return from malloc to the target type.
     
    Last edited: Aug 23, 2013
  6. Aug 23, 2013 #5

    jim mcnamara

    User Avatar

    Staff: Mentor

    @compuchip define "compile"
    So, No:
    It does run and does not segfault. Here is why: the compiler detected statically defined UB and then did what it felt like doing -- 5/0; has no lvalue so it was ignored assember output, notice please NO division:
     
    Last edited: Aug 23, 2013
  7. Aug 23, 2013 #6

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    On your computer and with your compiler, that is. Undefined behavior gives the compiler and runtime environment free reign to do anything at all and still be compliant with the standard.
     
  8. Aug 23, 2013 #7

    jim mcnamara

    User Avatar

    Staff: Mentor

    You are correct - the compiler is not required by the standard to do anything - it is free to do as it pleases. Or nothing. It could also reformat all of the filesystems on the computer. It chose to emit a NOP.
     
  9. Aug 23, 2013 #8

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Any comments on my post #3?
     
  10. Aug 23, 2013 #9
    For B, in addition to the sizeof(int) explicitly being set to 4, p can be garbage when returned. If the malloc returned NULL, the error needs to be processed. Perhaps you are out of memory.
     
  11. Aug 23, 2013 #10

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    The error *is* being handled in B. *p is set only if p isn't a null pointer. What kind of error handling do you want? A printed error message? A longjmp? That kind of error handling is best left to the calling routine in C. That malloc failed to allocate memory is indicated by the function returning a null pointer. That's typically just what the doctor ordered in C.
     
  12. Aug 24, 2013 #11
    Whoa. Mixed up there for B.

    B: int *p=malloc(4);
    would allocate space for 4 char, an array of 4 char so to speak, at least for K&R C. ( I am not sure how that plays out in later C ). Is a cast to int* necessary. This should work for early compiler editions of C.

    I was thinking that the int would or could overrun the space and thus garbage would result to a retrieved value of where where p is pointing later in the program.

    Anyways, you have allocated space of 4 char and assigning an int of unknown length to that space. What's that called - loose typing.

    Sorry.
     
  13. Aug 24, 2013 #12

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    No.

    malloc() returns a void* pointer that is either null or points to the start of a contiguous chunk of memory that is suitably aligned for use as any of the fundamental types (so use as an int* pointer is no problem) and whose size is measured in chars. In C (but not in C++), void* is compatible with any type. Any pointer can be converted to void* without a cast, and a void* can be converted to a pointer of some other type without a cast.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Is this undefined behavior in C?
  1. Undefined Reference (Replies: 10)

  2. To C or not to C (Replies: 31)

  3. C or C++? (Replies: 8)

Loading...