Jump to content

  • Log In with Google      Sign In   
  • Create Account


Why must a variable be initialized ?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
50 replies to this topic

#21 D.Chhetri   Members   -  Reputation: 181

Like
0Likes
Like

Posted 21 June 2011 - 08:53 AM

I generally agree with ApochPiQ, except I am happy to have code like this:

int x;
  std::cin >> x;

I would much rather not give x a value that will never be used. Notice that I declare x as late as possible, so it's trivial to verify that it will always be assigned a value.


If your going to do that, then I would suggest something like this:
int x = get<int>();

where get<int>() should be a template function that has proper error handling and all that extra stuff
Edge cases will show your design flaws in your code!
Visit my site
Visit my FaceBook
Visit my github

Sponsor:

#22 rip-off   Moderators   -  Reputation: 8166

Like
1Likes
Like

Posted 21 June 2011 - 09:02 AM

Code like reading from std::cin is an example of where "uninitialised" warnings tend to fail.
#include <iostream>
#include <sstream>
#include <string>

void test(const std::string &str) {
    std::stringstream stream(str);
    int i;
    if(stream >> i) {
        std::cout << "Success: " << i << '\n';
    } else {
        std::cout << "Failure: " << i << '\n';
    }
}

int main() {
   test("42"); 
   test("Hello");
}
GCC doesn't warn that "i" is uninitialised in the "else" branch. It obviously assumes that if you pass a reference or pointer to an uninitialised variable to a function, when it returns the variable has been initialised. Usually true, but not always.

Something like D.Chhetri 's code is preferable, we can throw an exception from get(), ensure the variable is correctly initialised or never used.

#23 way2lazy2care   Members   -  Reputation: 782

Like
0Likes
Like

Posted 21 June 2011 - 09:09 AM

Only for globals and statics where there is no overhead in having such an initialisation. There is no such thing as a "default" value for a stack-based POD.

Sure there is, it's just that the default value isn't assigned until initialization. If you default initialize a POD type, the value you get is 0

edit: and to further what you said in the same post but above the reply to me. Debug builds do all sorts of weird things specifically with variable initialization vs final builds. I'm pretty sure the debugger I use at work default initializes everything that isn't explicitly initialized and does a lot of attempts at mimicking compiler optimizations that are not what the final build does.

Not to say that behavior is bad, but just to back up your assertion that debug builds are not necessarily the best indicators of what is actually happening in this instance.

#24 ApochPiQ   Moderators   -  Reputation: 15202

Like
6Likes
Like

Posted 21 June 2011 - 09:51 AM

I'm going to go out on a limb here and argue that for, say, an int, there is no such thing as an insane value. 0 is no more a default value than 325434 or -26. On this basis, I don't see any advantage to initialising an int to 0 unless that happens to be the value I intend it to hold for other reasons.


The point isn't that int has an inherent invariant that's being preserved by initialization; the point is that your code's interpretation of that int has invariants. Are you using the int to represent a counter? Initialize to 0. Are you using the int to represent a multiplicative accumulator? Initialize to 1. Are you using the int to represent the answer to life, the universe, and everything? Initialize to 42.

Outside of highly contrived demonstration programs, every variable has a purpose, and that purpose comes with baggage: namely, assumptions about what values "make sense" and where the value should start off.

Even in highly contrived demonstration programs, having a known reliable initial value is preferable to a totally unpredictable, undefined value.

#25 Aardvajk   Crossbones+   -  Reputation: 5968

Like
0Likes
Like

Posted 21 June 2011 - 10:28 AM


Only for globals and statics where there is no overhead in having such an initialisation. There is no such thing as a "default" value for a stack-based POD.

Sure there is, it's just that the default value isn't assigned until initialization. If you default initialize a POD type, the value you get is 0

edit: and to further what you said in the same post but above the reply to me. Debug builds do all sorts of weird things specifically with variable initialization vs final builds. I'm pretty sure the debugger I use at work default initializes everything that isn't explicitly initialized and does a lot of attempts at mimicking compiler optimizations that are not what the final build does.

Not to say that behavior is bad, but just to back up your assertion that debug builds are not necessarily the best indicators of what is actually happening in this instance.


True enough, and for the record I have no idea why your post got four rate downs - I actually rated it up.

I admit I'm arguing a pretty silly point here really.

#26 SiCrane   Moderators   -  Reputation: 9575

Like
1Likes
Like

Posted 21 June 2011 - 10:31 AM

The C++ standard has a wonderfully confusing set of definitions that often are at ninety degrees with common sense. Section 8.5 of the current version contains all the details, but when POD types are "default initialized" that initialization is as if they had been initialized with 0's. On the other hand, "default initialization" only happens in a limited set of occasions, so "default initialization" doesn't happen by default most of the time. Most of the time when a POD variable lacks an initializer it is uninitialized rather than "default initialized" and thus has an indeterminate value.

IIRC, this particular confusion was the result of templates being added to the language and the desire for the expression T() to have a reliable meaning for when T is a POD type. Before that, C++ avoided the confusing term of default initialization and only had zero initialization and uninitialized values. I don't care enough to fact check this paragraph, however.

#27 way2lazy2care   Members   -  Reputation: 782

Like
0Likes
Like

Posted 21 June 2011 - 10:35 AM

The point isn't that int has an inherent invariant that's being preserved by initialization; the point is that your code's interpretation of that int has invariants. Are you using the int to represent a counter? Initialize to 0. Are you using the int to represent a multiplicative accumulator? Initialize to 1. Are you using the int to represent the answer to life, the universe, and everything? Initialize to 42.

Outside of highly contrived demonstration programs, every variable has a purpose, and that purpose comes with baggage: namely, assumptions about what values "make sense" and where the value should start off.

I actually initialize a lot of numbers to -1 because it's usually a good error condition that's pretty easy to check against when I'm not using them as counters. Though I could see 0 being similar as then you could use it without having to do a comparison. Not sure how good a practice that is though. I haven't really thought about it enough to make a good technical argument for doing it that way.

True enough, and for the record I have no idea why your post got four rate downs - I actually rated it up.

I admit I'm arguing a pretty silly point here really.

I'm guessing it's related to the argument being about default values without initialization and I could be interpreted as saying that it does have one of those, even though it is not what I said/meant :-p

#28 vaironl   Members   -  Reputation: 128

Like
1Likes
Like

Posted 21 June 2011 - 10:37 AM

Wow, Posted Image. This is allot of information from different perspectives , thanks to everyone for explaining the topic. :D

#29 mhagain   Crossbones+   -  Reputation: 7866

Like
1Likes
Like

Posted 21 June 2011 - 12:09 PM

Also very important for pointers. If you're not initializing your pointers, you will someday have trouble. If you're lucky it will just crash right away, but it could also pick up stack garbage that converts to a pointer to something that seems valid and that runs just fine for a while, but eventually crashes or exhibits weird behaviour in a totally unrelated part of your code.

It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#30 falcon93   Members   -  Reputation: 121

Like
0Likes
Like

Posted 21 June 2011 - 12:29 PM

The point isn't that int has an inherent invariant that's being preserved by initialization; the point is that your code's interpretation of that int has invariants. Are you using the int to represent a counter? Initialize to 0. Are you using the int to represent a multiplicative accumulator? Initialize to 1. Are you using the int to represent the answer to life, the universe, and everything? Initialize to 42.


I agree with you.

For those who doesn't agree; It's just about choosing logical numbers when you initilize. For example, if I have a timer that counts down from 60 seconds, wouldn't the most logical initialization number be 60 then?

When we are talking about initialization, I just have to put in a small question. When programming in C# XNA, how would you initialize your variables?
1. When you declare them
2. In the constructor
3. In the Initialize method

All three ways works. So far I've always initilized in the constructor as some classes recives parameters. Which one is the "more" correct?

#31 way2lazy2care   Members   -  Reputation: 782

Like
1Likes
Like

Posted 21 June 2011 - 01:46 PM

When we are talking about initialization, I just have to put in a small question. When programming in C# XNA, how would you initialize your variables?
1. When you declare them
2. In the constructor
3. In the Initialize method

All three ways works. So far I've always initilized in the constructor as some classes recives parameters. Which one is the "more" correct?


well C# is kind of different. C# default initializes every member variable to 0 or null if they are a value type or reference type respectively. Personally I would do it in the constructor for 99% of the cases. If you are going to fill it with a meaningful value in the initialize function vs constructor (if they have to be different), I would still give it some default value in the constructor.

One time I might not is if I am perhaps storing some sort of cache or something whose point of existing is that something shouldn't be stored in it all the time. This sentence sounded horrible in my head and I'm not quite sure how to say it better, so I am just going to stop. C# is managed and usually throws useful errors, so I find myself slightly less willing to learn the ins and outs perfectly.

#32 Mike.Popoloski   Crossbones+   -  Reputation: 2901

Like
3Likes
Like

Posted 21 June 2011 - 01:47 PM

Class members in C# are default initialized, so it's recommended that you don't do additional initialization on top of them unless you're going to be setting them to a different value than the default. Locals, on the other hand, require that you initialize them before first use, and the compiler will give you an error if you don't.
Mike Popoloski | Journal | SlimDX

#33 Aardvajk   Crossbones+   -  Reputation: 5968

Like
0Likes
Like

Posted 21 June 2011 - 03:32 PM

We are arguing apples and oranges here. If there is a logical initial value for a variable, then nobody is arguing that it shouldn't be initialised. The debate as I understood it was whether you should initialise a local POD if you are going to then overwrite that value with another one prior to first-read.

I actually agree with iMalc's comment on the last page - I've been struggling to think of an example of an instance where you actually need to declare a variable prior to knowing what it needs to contain, apart from the idiomatic std::cin example. In that example, given that you would be checking the fail state of the stream rather than the contents of the variable for an error, I still maintain that there is little point giving an int a "default" value prior to the read. If you are incorrectly assuming the variable contains a valid value, the error is not checking the stream for a fail, not not default initialising the variable.

But of course it is irrelevant with a modern compiler, so I'll still probably sleep okay tonight. :)

#34 ApochPiQ   Moderators   -  Reputation: 15202

Like
5Likes
Like

Posted 21 June 2011 - 04:33 PM

Resource acquisition is initialization. This is a deep mantra of C++ and should be taken to heart.

If you don't need a resource yet (POD variable, in this case) don't define it. If you do need it, initialize it. Period.


Nobody's saying you should initialize and then overwrite and then read; I'm saying you should initialize with preference to assigning over top of an uninitialized value. This has serious implications for non-POD types so doing it for all types is a good habit. I disagree fundamentally with the std::cin example for reasons I already provided, as well as the fact that a "real" system should be wrapping error checking for potential stream failures into a template function or something, as someone else already indicated as well.

In short, in properly written code, the initialize-write-read situation should never occur in the first place. You either have initialize-read, or you don't define the variable/object at all.

#35 blewisjr   Members   -  Reputation: 620

Like
0Likes
Like

Posted 21 June 2011 - 04:58 PM

Resource acquisition is initialization. This is a deep mantra of C++ and should be taken to heart.

If you don't need a resource yet (POD variable, in this case) don't define it. If you do need it, initialize it. Period.


Nobody's saying you should initialize and then overwrite and then read; I'm saying you should initialize with preference to assigning over top of an uninitialized value. This has serious implications for non-POD types so doing it for all types is a good habit. I disagree fundamentally with the std::cin example for reasons I already provided, as well as the fact that a "real" system should be wrapping error checking for potential stream failures into a template function or something, as someone else already indicated as well.

In short, in properly written code, the initialize-write-read situation should never occur in the first place. You either have initialize-read, or you don't define the variable/object at all.


I totally agree with this right here. This is the one thing that every programmer should take to heart. When I code I create the variable when I need it I don't declare/initialize it before that moment. This is actually one of the biggest advantages C++ has over C. In C prior to C99 I believe correct me if I am wrong but you MUST declare all your variables at the top of a function before code. With C++ and C99 you can declare your variables anywhere.

A majority of the time when I declare my variables it is when I need it and it is receiving a variable from somewhere that it should have. You see this in for loops when you use for(int i = 0; i <= 20; i++) or even more often the variable receives something from a function int i = add(3, 5); If you sit down and look at proper C++ usually this is how it is done period. If you don't need a variable at that exact moment why waste memory leaving it lay around till later.

#36 Storyyeller   Members   -  Reputation: 212

Like
0Likes
Like

Posted 21 June 2011 - 05:56 PM

Well I can think of a few examples. For example, what if the initialization code is conditional, but you need access to the variable in the enclosing scope?

int f;

switch (x)
{
	case 0: f = someval; break;
	case 1: f = someotherval; break;
	default: f = somethirdval; break;
}

//value of f used here

I trust exceptions about as far as I can throw them.

#37 Rattrap   Members   -  Reputation: 1625

Like
1Likes
Like

Posted 21 June 2011 - 06:16 PM

Well I can think of a few examples. For example, what if the initialization code is conditional, but you need access to the variable in the enclosing scope?

int f;

switch (x)
{
    case 0: f = someval; break;
    case 1: f = someotherval; break;
    default: f = somethirdval; break;
}

//value of f used here


This is the kind of code where the new lambda support in C++0x can come into play:

int f = [&x]() -> int
{
switch (x)
{
    case 0: return someval;
    case 1: return someotherval;
    default: return somethirdval;
}
}();

You get the best of both worlds. You can even declare f const if you want.

#38 Storyyeller   Members   -  Reputation: 212

Like
0Likes
Like

Posted 21 June 2011 - 08:32 PM

What if the switch statement calculates multiple values?
I guess you could create a special struct and make a function that returns it, but that is starting to get unnecessarily complicated.

Also, what if you're stuck with an older compiler that doens't have full C++0x support?
I trust exceptions about as far as I can throw them.

#39 Rattrap   Members   -  Reputation: 1625

Like
1Likes
Like

Posted 21 June 2011 - 08:44 PM

What if the switch statement calculates multiple values?
I guess you could create a special struct and make a function that returns it, but that is starting to get unnecessarily complicated.


It is just syntactic sugar. It allows you to define a local function that returns the desired value to assign at declaration. It can return anything a regular function can, like a struct or a tuple. The lambdas allow you to put the value generation code along with the definition.

Also, what if you're stuck with an older compiler that doens't have full C++0x support?


As mentioned before, lambdas are just syntactic sugar, allowing the use of local functions. You could replace the lambda with a function defined else where. The lambda can just make it eaiser to see what is going to get assigned. You could also look into using boost-lambdas.

#40 way2lazy2care   Members   -  Reputation: 782

Like
1Likes
Like

Posted 21 June 2011 - 08:55 PM

Well I can think of a few examples. For example, what if the initialization code is conditional, but you need access to the variable in the enclosing scope?

int f;

switch (x)
{
	case 0: f = someval; break;
	case 1: f = someotherval; break;
	default: f = somethirdval; break;
}

//value of f used here


This is all well and good if you are the only person working on the code, but that is rarely the case. What happens when some dumb guy throws in a third case that doesn't set f? What happens 3 years down the line when there are 100 lines of code between the switch and where f gets used? What happens when the function gets split up or parts of it get commented out? What happens if all of these things happen?

A lot of good programming practice can be thrown out the window when you are working with 1-3 programmers. Once you have people fidgeting with the whole codebase frequently, these tiny things become important.

DISCLAIMER: I have definitely not been super diligent on RAII in the past, and it does become a pain.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS