Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler not detecting obviously uninitialized variable

Tags:

c

All C compilers I've tried won't detect uninitialized variables in the code snippet below. Yet the case is obvious here.

Don't bother about the functionality of this snippet. It's not real code, and I stripped it down for the investigation of this issue.

BOOL NearEqual (int tauxprecis, int max, int value) {   int tauxtrouve;      // Not initialized at this point   int totaldiff;       // Not initialized at this point    for (int i = 0; i < max; i++)   {     if (2 < totaldiff)  // At this point totaldiff is not initialized     {       totaldiff = 2;       tauxtrouve = value;  // Commenting this line out will produce warning     }   }    return tauxtrouve == tauxprecis ;  // At this point tauxtrouve is potentially                                      // not initialized. } 

On the other hand, if I comment out tauxtrouve = value ;, I get the "local variable 'tauxtrouve' used without having been initialized" warning.

I tried these compilers:

  • GCC 4.9.2 with -Wall -WExtra
  • Microsoft Visual C++ 2013 with all warnings enabled
like image 987
Jabberwocky Avatar asked Nov 21 '14 14:11

Jabberwocky


People also ask

Why is my variable uninitialized?

An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.

What happens if variable is not initialised?

If a variable is declared but not initialized or uninitialized and if those variables are trying to print, then, it will return 0 or some garbage value. Whenever we declare a variable, a location is allocated to that variable.

Why are uninitialized variables bad?

"Uninitialized variables contain some value" is a incorrect statement which unfortunately is teached. A program who access an uninitialized variable has Undefined Behavior, which means it can have any behavior.

What happens if you print an uninitialized variable?

There is no "result of undefined behaviour". The behaviour is undefined.

What happens if you use an uninitialized variable?

Using uninitialized variables is one of the most common mistakes that novice programmers make, and unfortunately, it can also be one of the most challenging to debug (because the program may run fine anyway if the uninitialized variable happened to get assigned to a spot of memory that had a reasonable value in it, like 0).

Why do I get a compile-time error when using a variable?

Most modern compilers will attempt to detect if a variable is being used without being given a value. If they are able to detect this, they will generally issue a compile-time error. For example, compiling the above program on Visual Studio produced the following warning:

Why are variables not initialized in C++?

Uninitialized variables Unlike some programming languages, C/C++ does not initialize most variables to a given value (such as zero) automatically. Thus when a variable is assigned a memory location by the compiler, the default value of that variable is whatever (garbage) value happens to already be in that memory location!

What happens if you use a variable that has no known value?

In this case, the C++ language doesn’t have any rules determining what happens if you use the value of a variable that has not been given a known value. Consequently, if you actually do this, undefined behavior will result. Code implementing undefined behavior may exhibit any of the following symptoms:


2 Answers

Yes, it should raise a warning about that uninitialized variable, but it's a GCC bug. The example given there is:

unsigned bmp_iter_set (); int something (void);  void bitmap_print_value_set (void) {     unsigned first;      for (; bmp_iter_set (); )     {         if (!first)             something ();         first = 0;     } } 

And diagnosed with -O2 -W -Wall.

Unfortunately, this year is the 10 year anniversary of this bug!

like image 29
haccks Avatar answered Oct 05 '22 04:10

haccks


The obviousness with which this variable is not initialized is overstated. Path analysis costs time and your compiler vendors either didn't want to implement the feature or thought it would cost you too much time -- or you just didn't explicitly opt-in.

For example, with clang:

$ clang -Wall -Wextra -c obvious.c  $ clang -Wall -Wextra --analyze -c obvious.c  obvious.c:9:11: warning: The right operand of '<' is a garbage value     if (2 < totaldiff)  // at this point totaldiff is not initialized           ^ ~~~~~~~~~ obvious.c:16:21: warning: The left operand of '==' is a garbage value   return tauxtrouve == tauxprecis ;  // at this point tauxtrouve is potentially          ~~~~~~~~~~ ^ 2 warnings generated. 

The difference in execution time for these naïve examples is negligible. But imagine a translation unit with thousands of lines, tens of functions, each with loops and heavy nesting. The number of paths quickly compounds and becomes a large burden to analyze whether or not the first iteration through the loop whether the assignment will occur prior to that comparison.


EDIT: @Matthieu points out that with LLVM/clang, the path analysis required to find use-of-uninitialized value does not compound as nesting increases because of the SSA notation used by the IR.

It wasn't as simple as "-S -emit-llvm" like I'd hoped, but I found the SSA-notation output he described. I'll be honest, I'm not familiar enough with LLVM IR to be sure, but I'll take Matthieu's word for it.

Bottom line: use clang with --analyze, or convince someone to fix the gcc bug.

; Function Attrs: nounwind uwtable define i32 @NearEqual(i32 %tauxprecis, i32 %max, i32 %value) #0 {   br label %1  ; <label>:1                                       ; preds = %7, %0   %tauxtrouve.0 = phi i32 [ undef, %0 ], [ %tauxtrouve.1, %7 ]   %i.0 = phi i32 [ 0, %0 ], [ %8, %7 ]   %2 = icmp slt i32 %i.0, %max   br i1 %2, label %3, label %9  ; <label>:3                                       ; preds = %1   %4 = icmp slt i32 2, 2   br i1 %4, label %5, label %6  ; <label>:5                                       ; preds = %3   br label %6  ; <label>:6                                       ; preds = %5, %3   %tauxtrouve.1 = phi i32 [ %value, %5 ], [ %tauxtrouve.0, %3 ]   br label %7  ; <label>:7                                       ; preds = %6   %8 = add nsw i32 %i.0, 1   br label %1  ; <label>:9                                       ; preds = %1   %10 = icmp eq i32 %tauxtrouve.0, %tauxprecis   %11 = zext i1 %10 to i32   ret i32 %11 } 
like image 102
Brian Cain Avatar answered Oct 05 '22 04:10

Brian Cain