8

I understand that local variables are not initialised automatically in C++, so before using them, you should always assign a value to them. However, at least in simple cases, the compiler should warn you in case you forget it. I'm more or less relying on and referring to this article.

Given this program, I'd assume to get a warning when sending x to std::cout...

#include <iostream>

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

  int x;
  std::cout << x;

  return 0;
}

...but no warning pops up. If, however, I run the static analyzer, I do get the expected warning: Function call argument is an uninitialized value.

I compile & run using Xcode 5.1 with the Apple LLVM 5.1 compiler. I use the standard build settings from Xcode's command line project template (C++), the language dialects are set to GNU99 (for C) and GNU++11 (C++).

The Uninitialized Variables option is set to Yes (Aggressive) (-Wconditional-uninitialized). Switching to just Yes (-Wuninitialized) raises the warning: Variable 'x' is uninitialized when used here. Question Part 1: Why does the warning not show with the default setting (-Wconditional-uninitialized)? The documentation in Xcode suggests that the aggressive option finds more issues:

You can toggle between the normal uninitialized value checking or the more aggressive (conservative) checking which finds more issues but the checking is much stricter.

Strangely enough, when I run the program, the value is always set to 0, so for some reason, it seems to be initialised. Question Part 2: Why is that?

izhang05
  • 744
  • 2
  • 11
  • 26
hagi
  • 11,503
  • 3
  • 35
  • 48
  • 2
    "seems to be initialised" doesn't guarantee it always be initialised. – Bryan Chen Mar 19 '14 at 21:58
  • True. I just assumed to get complete garbage output in 99% of all cases and was wondering if any part of my toolchain/settings actually does ensure it is initialised? – hagi Mar 19 '14 at 22:08
  • 2
    Just found this which clarifies the second point: *To compound problems finding uninitialized variables, variable declared when running the program in a debugger are typically zeroed. This means your program may work fine every time when run in a debugger, but crash intermittently in release mode!* ([Source](http://www.learncpp.com/cpp-programming/eight-c-programming-mistakes-the-compiler-wont-catch/)). The "expected" garbage values show up as soon as I switch to release builds... – hagi Mar 19 '14 at 22:13

2 Answers2

4
  1. Why not the warning?

Using clang with -Wall on my system correctly warns about the error. Apparently, the default settings do not include -Wall (may be to avoid generating warnings with correct code that was written before some of the warnings were introduced).

In general, you're however going to be in trouble if you rely on the compiler to help you with sloppy programming. Typing in code without thinking carefully and hoping the compiler will tell you all mistakes is bad in any language but a true total disaster with C++. The main philosophy of C++ is simply that the programmer doesn't make any error, so just don't make them ;-)

Think carefully and also always work with -Wall if you can.

  1. Why is initialized?

Apparently, you've not understood what is the meaning of "undefined behavior". It doesn't mean the program crashes, it doesn't mean it will do anything funny. It means it can do anything and normally the programs do whatever will create the most problems for you in the future.

Often this most dangerous behavior is to make it look as if everything is fine (e.g. that your variable is indeed initialized). The bad values will only show once you put that code in production or only when you show your program running in front of a vast audience. At that point, the value will be different and the video of your public crash will go viral on youtube, your wife will change the door locks and even your parents will not answer your phone calls.

Just initialize your variables; it's better :-)

6502
  • 112,025
  • 15
  • 165
  • 265
  • `-Wall` indeed does the job. Just to clarify: I didn't mean to question the necessity of initialising variables, I just couldn't see why the problem wasn't reported by the default compiler settings. Thanks for your help & your explanations. – hagi Mar 19 '14 at 22:19
  • "The main philosophy of C++ is simply that the programmer doesn't make any error", then why even bother having a type system at all? Why not just leave everything as `void *` and let programs compile freely to garbage instructions? – Khoa Vo Nov 26 '22 at 01:31
  • @KhoaVo At compile time the compiler does a lot of checks (if you enable them) but at run time they must be zero-cost, not even a nanosecond. For example checking for accessing out of an array is not done because it's not free and correct programs don't do that. Out of bounds access is UB... not segfault; it means it will happen what will happen (often nothing as hardware doesn't care). Even about every pointer being `void *` you can do that in C++ if needed, and you can even embed handwritten machine code because sometimes the language is too limited to express what assumptions can be done. – 6502 Nov 26 '22 at 06:45
  • @KhoaVo: There are many (most?) languages in which the very concept of Undefined Behavior does not exist and instead of "UB daemons" you have "runtime error angels" that for example guarantee a program stop when you access outside of an array. C++ is different... there are almost no checks at runtime so I often describe this as "C++ assumes programmers make no mistakes" (meaning you must be much more careful when writing code). I don't think I can explain this in simpler terms... if you still don't get it I don't really know how to help you. Sorry. – 6502 Nov 26 '22 at 11:46
1

It's necessary but enough to turn on -Wall to make our code safe.

e.g. The following code, under MacOS's g++(it's clang) will print out 0, but it is definitely a UB:

#include <stdio.h>
#include <iostream>

using namespace std;

class Blob {
public:
    int age;
    void hello() { printf("hello\n"); }
};

int main() {
    Blob a;
    //a.hello();
    cout << a.age << endl;

    return 0;
}
ChrisZZ
  • 1,521
  • 2
  • 17
  • 24
  • I'm getting the same behavior on Linux with `clang++` 13.0.0. `g++` 11.1.0 reports correctly the problem instead. – MirkoBanchi Nov 28 '21 at 13:45