7

In a program I am writing I currently have several uninitialised variables in my .h files, all of which are initialised at run-time. However, in Visual Studio it warns me every time I do this to "Always initialise a member variable" despite how seemingly pointless it feels to do so. I am well aware that attempting to use a variable when uninitialised will lead to undefined behaviour, but as far as I know, this can be avoided by not doing so. Am I overlooking something?

Thanks.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
Telf
  • 77
  • 3
  • 5
    I usually think of security measures like that the same as a pilot checklist. Yes, they know how to fly a plane. But that checklist, and forcing yourself to legitimately go through it, has saved lives. Just initialize the variables, in time one will eventually slip through the cracks and cause issues. – sweenish Feb 21 '20 at 15:28
  • 4
    "Am I overlooking something?" If you initialize, you are *sure* you don't. – Peter - Reinstate Monica Feb 21 '20 at 15:28
  • It's a design pattern meant to make it easier to always do the right thing. If the rule is, "Initialize variables, ...unless you know you won't be using them," then it's easier to make a mistake one day. If you always initialize variables you don't need to think about it. Who does it hurt to initialize those variables? – JohnFilleau Feb 21 '20 at 15:29
  • 1
    To be more specific: I'm working in transportation, SIL-4, and we test every line, every branch of the program. That is a huge effort.To eliminate one source of errors with a simple, cheap measure like "always initialize" is valuable. Proving that no path skips a lazy initialization is hard. You are right though that not all programs need to be correct: My TV set top box freezes about once a week or two. That's ok. It does not control an airplane. – Peter - Reinstate Monica Feb 21 '20 at 15:32
  • It warns you to initialize member variables. If you're not initializing them in the constructor, then you're doing something weird.. I'm fairly certain by the time the constructor finishes, your variables should have been initialized or have a value that you have defined. – Brandon Feb 21 '20 at 15:33
  • 1
    *variables in my .h files* Unless they are member variables in a class, I recommend against this. It's usually a quick way to get multiple definition errors. – user4581301 Feb 21 '20 at 15:52
  • @Telf What do you mean by uninitialized? As in the value was not explicitly set for the variable or the variable was never declared? – spicy.dll Feb 21 '20 at 16:09
  • Why even bother asking this? Since you said that you know that uninitialized variables are bad, just initialized them. It's that simple. – Gabriel Jan 06 '23 at 14:57

4 Answers4

7

These variables could contain any value if you don't initialize them and reading them in an uninitialized stated is undefined behavior. (except if they are zero initalized)

And if you forgot to initialize one of them, and reading from it by accident results in the value you expect it should have on your current system configuration (due to undefined behavior), then your program might behave unpredictable/unexpected after a system update, on a different system or when you do changes in your code.

And these kinds of errors are hard to debug. So even if you set them at runtime it is suggested to initialize them to known values so that you have a controlled environment with predictable behavior.

There are a few exceptions, e.g. if you set the variable right after you declared it and you can't set it directly, like if you set its value using a streaming operator.

t.niese
  • 39,256
  • 9
  • 74
  • 101
  • 1
    "These variables could contain any value if you don't initialize them." Wrong. Undefined behavior means anything can happen. The program could simply crash. – bolov Feb 21 '20 at 15:31
  • Actually @bolov , it depends on what the variables are used for. If they are function pointers, this is a security issue. If its an integer, its value is simply whatever is on the stack. – spicy.dll Feb 21 '20 at 15:35
  • 2
    @MasonSchmidgall Even with simple uninitialized integers, the UB they cause can make a compiler do weird stuff in release builds. – HolyBlackCat Feb 21 '20 at 15:36
  • 1
    @MasonSchmidgall wrong. "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. – bolov Feb 21 '20 at 15:59
  • 1
    @bolov Perhaps we're on the wrong page. Uninitialized as in value not set or uninitialized as in space not allocated. If it's the ladder, that's 100% undefined behavior. However, if it is allocated space, it always has some value, even if it was not set by the programmer. – spicy.dll Feb 21 '20 at 16:06
  • @MasonSchmidgall I am talking in terms of what the C++ language specifies. You maybe are talking about certain compiler implementations on certain architectures. The C++ standard deals with an abstract machine. – bolov Feb 21 '20 at 16:09
  • @MasonSchmidgall please see [my answer](https://stackoverflow.com/a/60342706/2805305) to see a clear example why your reasoning is wrong. – bolov Feb 21 '20 at 16:28
  • @MasonSchmidgall It's UB either way, simply because the standard says so. Most of the time reading an uninitialized variable might simply give you a garbage value, yes, but formally doing it can have any other effect. Some compilers are more prone to getting confused by this kind of UB than others. – HolyBlackCat Feb 21 '20 at 18:14
1

It's a safety measure to not allow uninitialized variables, witch is a good thing, but if you are sure of what you are doing and you make sure your variables are always initialzed before use, you can turn this off, right click on your project in solution explorer -> properties -> C/C++ -> SDL checks, this should be marked as NO. It comes as YES by default.

Note that these compile-time checks do more than just check for unitialized variables, so before you turn this off I advise reading https://learn.microsoft.com/en-us/cpp/build/reference/sdl-enable-additional-security-checks?view=vs-2019

You can also disable a specific warning in you code using warning pragma

Personally I keep these on because IMO in the tradeoff safety/annoyance I prefer safety, but I reckon that someone else can have a different opinion.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • This will turn off more than [uninitialized variable warnings](https://learn.microsoft.com/en-us/cpp/build/reference/sdl-enable-additional-security-checks?view=vs-2019). It's probably better to only disable a specific warning code(s) if it bothers them so much. – Blastfurnace Feb 21 '20 at 15:43
  • @Blastfurnace yes but you can keep the warnings on, in the same menu you have warnings setup, you can keep a -Wall warning level, and upwards to three other warning levels, I think for a experienced user this can be enough. Personally I keep this on because I like safety, and it's not that troublesome to initialize variables uppon declaration. – anastaciu Feb 21 '20 at 15:48
  • The `/sdl` option does considerably more than just warn about uninitialized variables which your answer completely ignores. Disabling a specific warning code is much better advice than bluntly turning off multiple compile-time and run-time features. – Blastfurnace Feb 21 '20 at 15:55
  • @Blastfurnace, Yes, you're right, I'll make sure to note that in my answer. – anastaciu Feb 21 '20 at 16:11
1

You have not included the source so we have to guess about why it happens, and I can see possible reasons with different solutions (except just zero-initializing everything):

  1. You don't initialize at the start of the constructor, but you combine member initialization with some other code that calls some functions for the not fully initialized object. That's a mess - and you never know when some functions will call another function using some non-initialized member. If you really need this, don't send in the entire object - but only the parts you need (might need more refactoring).
  2. You have the initialization in an Init-function. Just use the recent C++-feature of having one constructor call another instead.
  3. You don't initialize some members in the constructor, but even later. If you really don't want to initialize it having a pointer (or std::unique_ptr) containing that data, and create it when needed; or don't have it in the object.
Hans Olsson
  • 11,123
  • 15
  • 38
  • And eEven if you do have the code, UB makes it hard to enumerate all the possibilities. – user4581301 Feb 21 '20 at 15:54
  • I assumed it wasn't UB; just code that triggered the compiler diagnostic. Obviously we cannot know without seeing the code - and in some cases the analysis will be complicated even with the code. – Hans Olsson Feb 21 '20 at 16:03
0

There are two parts to this question: first is reading uninitialized variables dangerous and second is defining variables uninitialized dangerous even if I make sure I never access uninitialized variables.

What are the dangers of accessing uninitialized variables?

With very few exceptions, accessing an uninitialized variable makes the whole program have Undefined Behavior. There is a common misconception (which unfortunately is taught) that uninitialized variables have "garbage values" and so reading an uninitialized variable will result in reading some value. This is completely false. Undefined Behavior means the program can have any behavior: it can crash, it can behave as the variable has some value, it can pretend the variable doesn't even exist or all sorts of weird behaviors.

For instance:

void foo();
void bar();

void test(bool cond)
{
    int a; // uninitialized

    if (cond)
    {
        a = 24;
    }

    if (a == 24)
    {
        foo();
    }
    else
    {
        bar();
    }
}

What is the result of calling the above function with true? What about with false?

test(true) will cleary call foo().

What about test(false)? If you answer: "Well it depends on what garbage value is in variable a, if it is 24 it will call foo, else it will call bar" Then you are completely wrong.

If you call test(false) the program accesses an uninitialized variable and has Undefined Behavior, it is an illegal path and so the compilers are free to assume cond is never false (because otherwise the program would be illegal). And surprise surprise both gcc and clang with optimizations enabled actually do this and generate this assembly for the function:

test(bool):
        jmp     foo()

So don't do this! Never access uninitialized variable! It is undefined behavior and it's much much worse than "the variable has some garbage value". Furthermore, on your system could work as you expect, on other systems or with other compiler flags it can behave in unexpected ways.

What are the dangers of defining uninitialized variables if I make sure I always initialize them later, before accessing them?

Well, the program is correct from this respect, but the source code is prone to errors. You have to mentally burden yourself with always checking if somewhere you actually initialized the variable. And if you did forget to initialize a variable finding the bug will be difficult as you have a lot of variables in your code who are defined uninitialized.

As opposed, if you always initialize your variables you and the programmers after you have a much much easier job and ease of mind.

It's just a very very good practice.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • "*This is completely false.*" No, it is not. It is the way most compilers will handle that code when optimization passes are turned off, for good reason. Further, initializing variables is a good practice to avoid human errors (and it should have been the default just for this reason), but not for optimization purposes. – Acorn Sep 15 '20 at 08:59
  • @Acorn Accessing uninitialized variables is UB. Yes, its true that sometimes it will behave like it has a garbage value, but you cannot say that uninitialized variables get a garbage value. That is just one of the possible behavior, that's what UB is: it means it can have any behavior including crashes, or weird behaviors like I've shown. You misunderstood my point: I didn't say you should initialize variables because of optimizations. I gave an example to show weird behavior outside of "having a garbage value" to prove what UB means. – bolov Sep 15 '20 at 15:25
  • Actually, UB just means that the compiler gets to decide what to do. When optimization passes are turned off, compilers will generate code to read a garbage value from the stack pointer. My point is that saying it is "completely false" sounds like "that will never happen", when in fact it is what compilers do with optimizations turned off. In other words, UB does not mean "the program can have any behavior", but rather that the behavior is decided by the compiler -- a subtle but important point. – Acorn Sep 15 '20 at 15:46
  • Concerning the initialization, I haven't said you wrote that -- I merely pointed out that uninitialized variables have their use for optimization. – Acorn Sep 15 '20 at 15:47