Is this undefined behavior in C?

  • Thread starter jim mcnamara
  • Start date
  • Tags
    Behavior
In summary: SNED ~> gcc -S t.st.s: In function `main':t.s:5: warning: division by zerojmcnama@SNED ~> ./tjmcnama@SNED ~> echo $?13jmcnama@SNED ~> uname -aSunOS SNED 5
  • #1
jim mcnamara
Mentor
4,770
3,817
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:
Section 3.4:
behavior
external appearance or action

Section 3.4.3:

undefined behavior
behavior, upon use of a nonportable or erroneous program
construct or of erroneous data, for which this International Standard imposes no
requirements

NOTE: Possible undefined behavior ranges from ignoring the situation completely
with unpredictable results, to behaving during translation or program execution
in a documented manner characteristic of the environment (with or without the
issuance of a diagnostic message), to terminating a translation or execution
(with the issuance of a diagnostic message).
Code:
// 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:
Technology news on Phys.org
  • #2
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.
 
  • #3
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.
 
  • #4
CompuChip said:
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.
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.

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).
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:
  • #5
@compuchip define "compile"
So, No:
jmcnama@SNED ~> cat t.c
int main(void)
{
int i=13;
5/0;
return i;
}
jmcnama@SNED ~> cc t.c -o t
t.c: In function `main':
t.c:4: warning: division by zero
jmcnama@SNED ~> ./t
jmcnama@SNED ~>
jmcnama@SNED ~> echo $?
13
jmcnama@SNED ~> uname -a
SunOS SNED 5.10 Generic_144488-14 sun4u sparc SUNW,SPARC-Enterprise
jmcnama@SNED ~> gcc --version
gcc (GCC) 3.4.3 (csl-sol210-3_4-branch+sol_rpath)

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:
jmcnama@SNED ~> cc -S t.c
t.c: In function `main':
t.c:4: warning: division by zero
jmcnama@SNED ~> cat t.s
.file "t.c"
.section ".text"
.align 4
.global main
.type main, #function
.proc 04
main:
!#PROLOGUE# 0
save %sp, -120, %sp
!#PROLOGUE# 1
mov 13, %g1
st %g1, [%fp-20]
ld [%fp-20], %g1
mov %g1, %i0
ret
restore
.size main, .-main
.ident "GCC: (GNU) 3.4.3 (csl-sol210-3_4-branch+sol_rpath)"
 
Last edited:
  • #6
jim mcnamara said:
It does run and does not segfault.
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.
 
  • #7
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.
 
  • #8
Any comments on my post #3?
 
  • #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.
 
  • #10
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.
 
  • #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.
 
  • #12
256bits said:
Is a cast to int* necessary.
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.
 

Related to Is this undefined behavior in C?

1. What is undefined behavior in C?

Undefined behavior in C refers to situations where the behavior of a program is not defined by the C programming language. This means that the results of running the program may vary and cannot be predicted. It is often caused by writing code that violates the rules of the C language or by using uninitialized variables.

2. What are some examples of undefined behavior in C?

Some examples of undefined behavior in C include accessing uninitialized variables, using pointers that point to invalid memory locations, and dividing by zero. These actions may result in unexpected and unpredictable behavior, making it difficult to debug and fix the code.

3. How can I avoid undefined behavior in C?

To avoid undefined behavior in C, it is important to carefully follow the rules and guidelines of the C language. This includes properly initializing variables, avoiding out-of-bounds memory access, and checking for potential errors and edge cases. It is also helpful to use debugging tools and techniques to catch any potential issues.

4. Is undefined behavior always a bad thing in C?

While undefined behavior can lead to unpredictable outcomes and make debugging difficult, it is not always a bad thing in C. In some cases, it can be used for optimization purposes. However, it is important to use it carefully and only when necessary, as it can also introduce security vulnerabilities and other problems.

5. How can I debug undefined behavior in C?

Debugging undefined behavior in C can be challenging, but there are some techniques that can help. This includes using debugging tools like a debugger or memory checker, reviewing the code for any potential violations of C rules, and using test cases to isolate and identify the source of the issue. It can also be helpful to consult with other experienced C programmers for assistance.

Similar threads

  • Programming and Computer Science
Replies
4
Views
3K
  • Programming and Computer Science
Replies
5
Views
1K
  • Programming and Computer Science
Replies
11
Views
4K
  • Programming and Computer Science
Replies
4
Views
2K
  • Programming and Computer Science
Replies
4
Views
3K
  • Engineering and Comp Sci Homework Help
Replies
3
Views
4K
  • Programming and Computer Science
2
Replies
49
Views
10K
  • Programming and Computer Science
Replies
7
Views
12K
  • Programming and Computer Science
Replies
13
Views
6K
  • Engineering and Comp Sci Homework Help
Replies
1
Views
3K
Back
Top