4

I'm a little bit confused. As far as I know, if you declare an int in C, without initializing it, for e.g: int x;

so its value is indeterminate. So if we try to use it or should have undefined behavior.

So if i'm running the following code in VS2010 It crash the program.

int main(){
    int a;
    printf("%d\n",a);
    return 0;
}

Now lets take a look at the next code, which does not provide any warning and does not crash (why?)

void foo(int *var_handle){
    // do nothing
}

int main(){
    int a;
    foo(&a);
    printf("%d\n",a); // works, prints some big value
    return 0;
}

Can you explain the behavior of this? we only added a call to a function which does nothing at all, but now program wont crash.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
etaiso
  • 2,736
  • 3
  • 26
  • 38
  • 8
    "undefined behaviour" does not mean a guaranteed crash. If you perform operations with undefined behaviour, the result is.. well, undefined. – uba Mar 07 '13 at 10:36
  • I'm sure we get this exact same question at least once a day. – James M Mar 07 '13 at 10:36
  • Too localized, vote to close. Exploring why undefined behavior behaves in a certain way on a certain platform is completely pointless and yields no valuable knowledge. – Lundin Mar 07 '13 at 10:44
  • 3
    Why does it crash? Whatever value a holds, printf should just print it... the memory area for a is allocated correctly... – LtWorf Mar 07 '13 at 10:57
  • @Lundin: it doesn't *always* yield valuable knowledge. Often explaining UB does yield valuable knowledge. Understanding what UB your implementation produces helps find the bugs that cause unexpected behavior. For example, it is useful to know that a particular signal indicates that division by zero has been attempted, because it tells you to check your arithmetic. – Steve Jessop Mar 07 '13 at 10:59
  • @LtWorf: Most likely, the value in `a` is a trap representation. – John Bode Mar 07 '13 at 11:41
  • 1
    @JohnBode: Is there any documentation for Visual Studio 2010 that indicates it has any trap representation in the `int` type? – Eric Postpischil Mar 07 '13 at 12:29
  • @SteveJessop Maybe in some rare case. But the answer holds no value to any future reader. Who except the OP could possibly ever be interested in weird behavior of one particular compilation setup, on one particular Windows compiler, on an unknown version of Windows? There is little hope of recreating the same undefined behavior. – Lundin Mar 07 '13 at 12:36
  • 3
    @Lundin: That is equivalent to saying teachers should never give specific numbers in arithmetic exercises because who except one person could possibly ever be interested in the behavior of one particular sum, in particular commercial transaction, in some arbitrary city? There is little hope of recreating the same situation. The fact is that both numbers and computers have patterns, and studying examples illuminates rules. There is an absurd “atmosphere” on StackOverflow that computing is done only inside the abstract C model, and learning anything outside that model is useless. Nonsense. – Eric Postpischil Mar 07 '13 at 14:01
  • @EricPostpischil Mathematics are deterministic, undefined behavior is not. If this particular case of UB is so wonderfully educational and interesting, then by all means we should demand that the OP post their RAM memory model, read the circuit data sheets and see how the cells behave when not written to, how their ones decays to zeroes in relation to supply & refresh voltages. Then we shall accurately measure these voltages in the PC. We should then analyse the whole RAM of the PC at the exact point before the program is executed. Only then can we tell where the garbage values came from. – Lundin Mar 07 '13 at 15:47
  • @Lundin: Undefined behavior is not deterministic? Baloney. At the raw hardware level, there are a few uncontrolled things. But once you are in a modern operating system environment, nearly everything is defined, including memory and initial program state. It is not defined by the C standard, but it is determined by the operating system, by the start routine inserted by the linker, by the shell environment, and so on. Learning how these things work is useful. Preaching ignorance is harmful. – Eric Postpischil Mar 07 '13 at 15:53
  • @EricPostpischil There is a whole world outside your desktop PC however. When I power up the embedded system sitting next to me here and read the RAM cells, I get seemingly random values every time. If you want I can take 100 or so hex dumps of the RAM and mail them? So that you can determine the deterministic pattern for yourself? – Lundin Mar 07 '13 at 16:03
  • @Lundin: It is senseless to argue that we should not learn about some software and environments because other software and environments also exist. – Eric Postpischil Mar 07 '13 at 16:09
  • @Lundin: The goal here is not to answer “How can I always, or almost always, predict and diagnose undefined behavior?” The goal is to answer questions like “How is a program started in a typical operating system?”, “Does Visual Studio have trap representations in the `int` type?”, “What clues does the observed behavior give me about the source of the error in my source code?”, “What information is there about my C implementation beyond the C standard that can be used to make deductions about bugs?” – Eric Postpischil Mar 07 '13 at 16:22
  • No correct answer here yet, so closing as a dupe. – Deduplicator Jul 03 '15 at 16:13
  • @JohnBode I have to disagree. The likelihood that `a` contains a trap representation is precisely 0. There's a tiny handful of programmers in the world using processors which have trap representations, and none of those programmers is posting beginner's questions to Stack Overflow. – Steve Summit Feb 12 '21 at 22:26
  • @Lundin, Eric: I think you're both right. Beginners who ask "What should this undefined behavior do?" or "Why did it do X?" or "Why didn't it do Y?" are usually ignorant of what undefined behavior actually implies, and need to be led gently towards a better understanding. But at the same time, everyone is curious, and curiosity is good, and whether or not the question of "wait, how could it do *Z*?" may be of any long-term value, it can certainly help the motivated asker to learn any number of useful things. So as much as we want to, we can't always dismiss these questions out of hand. – Steve Summit Feb 12 '21 at 22:33
  • @EricPostpischil I've stopped believing, however, that computer software is deterministic any more. Modern software may not be quite complicated enough to have spontaneously achieved sentience, but it has definitely achieved chaos. Practically every day I have a piece of software (usually one written in Redmond, alas) perform *completely* differently on consecutive invocations. It's maddening, and it's really making me question whatever faith I had in determinism. – Steve Summit Feb 12 '21 at 22:36
  • @SteveSummit I have since this old post was posten written this self-answered Q&A about UB that can be used for such cases where beginners are looking to understand the outcome of certain UB: [What is undefined behavior and how does it work?](https://software.codidact.com/posts/277486) – Lundin Feb 15 '21 at 07:20
  • I have also since the point this was written learned more about various cases of poorly-defined behavior related to uninitialized variables. There are cases indeed where reading an indeterminate value is just unspecified behavior, rather than UB. And the variable use (address taken or not) matters too. This post is in fact a dupe of [this](https://stackoverflow.com/q/11962457/584518). – Lundin Feb 15 '21 at 07:22

6 Answers6

9

Reading the value of an uninitialized variable leads to undefined behavior. And undefined behavior means that it can crash. It doesn't mean it will or it is obliged to crash.

An uninitialized variable has unspecified value - it's just unknown what its value is. So in practice, with any sane implementation, this kind of code will presumably never crash. There's a valid memory address backing the variable, it has some garbage content, printf() reads it without problem, interprets it as an integer and prints it, that's all.

  • Repeat from comments of question: Is uninitialized local variable really undefined behaviour in C standard? Or is it just undefined value, in practice comparable to for example setting random value? If it is fully UB, then followup: what about `char s[100]; s[0]=0;`, is that valid code? At which point `s` becomes a 100 char big valid block of memory? – hyde Mar 07 '13 at 11:38
  • 1
    @hyde if I recall correctly, then it is undefined behavior (which I don't find quite logical, by the way). And in case of arrays, I'd say they're only the initialized elements of an array that count as, well, initialized. To initialize an entire array, use `char arr[100] = { 0 };`. Otherwise, `char a[100]; a[0] = 0; printf("%c", a[1]);` still ain't no good. –  Mar 07 '13 at 11:43
  • Interestingly, with gcc 4.7.2 with `-Wall -Wextra -pedantic -std=c99`, accessing the only element of `char s[1]` before initializing it does not give warning. I guess I should really dig out the standard and see if they say something different about arrays. They probably do, as plain `s` must certainly be defined (so it can be passed to `setmem`, for example) even if contents are uninitialized. – hyde Mar 07 '13 at 12:00
  • Getting quite discussiony, but if `foo` in question actually did `*var_handle = 0`, would the program then become defined behaviour? IOW, is it ok to pass address of uninitialized local variable to a function which will initialize it through pointer? – hyde Mar 07 '13 at 12:19
  • "An uninitialized variable has unspecified value" <- Not necessarily. It has an _indeterminate_ value, which is either a trap representation or an unspecified value. In practice, trap representations are rare exceptions, so betting on an unspecified value will win more often than not. Nevertheless, using an object with automatic storage duration while its value is indeterminate is UB, says Annex J2. – Daniel Fischer Mar 07 '13 at 16:00
  • I understand the undefined behavior of C in this case but my question is why the fact that I called foo caused my program to behave differently? Is there any error in this code except the uninitialized variable? – etaiso Mar 07 '13 at 17:25
  • 1
    @etaiso Your first program "shouldn't" crash at all. ("shouldn't" as in: there is no trap representation for `int`s on any platform Windows runs on, as far as I know, and what effectively happens in that scenario is that the bits that happened to be in the place where `a` was allocated are interpreted as an `int` and printed in any half-sane implementation, and any implementation I ever heard of. It's just that the standard allows for weird platforms that might have trap representations, and if the bits happen to be one, it would crash on those. So the standard says UB.) – Daniel Fischer Mar 07 '13 at 17:35
  • Can you reproduce the crash with a new project? I would expect that something else crashed actually. – Daniel Fischer Mar 07 '13 at 17:37
5

Using an uninitialized value does not directly cause undefined behavior.

Per C 2011 (n1570 draft) 6.7.9 10, an uninitialized object with automatic storage duration has indeterminate value. Per 3.19.2 1, an indeterminate value is either an unspecified value or a trap representation. Similar text appears in C 1999.

If the object has a trap representation, then undefined behavior may occur. However, if the object has an unspecified value, then program must behave has if the object has some determinate value; it is merely not specified which value the object has. The program is not permitted to crash merely because the value is unspecified.

It is surprising that you report the simple program shown crashes in Visual Studio 2010, because I do not expect that the int type has any trap representations in Visual Studio 2010. It may be that some source file other than what you expected was compiled and crashed or that you have enabled special debugging features in Visual Studio 2010 that attempt to track uninitialized objects (but fail in the second case where you use foo).

I suggest you repeat the test from scratch, pasting the code you displayed in this question into a new file and compiling that new file with default options.

Of course, Visual Studio 2010 does not conform to the C standard, not even the old 1999 standard, so it is not bound to obey the above clauses. In effect, everything about Visual Studio 2010 is undefined behavior with regard to the C standard.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    Had to google "trap representation", here's a nice link for benefit of others: http://stackoverflow.com/questions/6725809/trap-representation – hyde Mar 07 '13 at 12:49
  • They only speak about such problems with float numbers, not with int – LtWorf Mar 07 '13 at 14:40
  • 3
    The funny thing is, I was about to argue with @H2CO3's answer as well, but then I read the (informative) Annex J of C11 where they list all UB. You will find this listed as UB: `- The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8).` I cannot find anything about UB in those chapters though, apart from the trap representation example. – Lundin Mar 07 '13 at 15:57
1

It's undefined behaviour, meaning that anything could happen. Literally anything. The behavior is just not defined.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
1

You could try this. I don't know if it is strictly undefined behaviour or not, but I can't think of a way for a compiler to actually behave in undefined manner and still be compliant with C standard, at least if foo is in different compilation unit (~source file), because then compiler does not know it would be allowed to produce undefined behaviour ;).

void foo(int *var_handle){
    // do something to var_handle, or maybe nothing, who knows
}

int main(){
    int a[1];
    foo(a);
    printf("%d\n", a[0]);
    return 0;
}

Edit: Further thoughts:

I'm fairly certain it's ok to use a function to initialize uninitialized local variable, by giving non-const pointer to the local variable to the function. So, merely getting address of a local variable makes it defined variable with undefined value, as far as compiler is concerned. Compiler can not know if the function actually sets the value or not (function might be in a library).

But this just explains why it avoids the crash. It could still be, that if function is allowed to be inlined, and did nothing, optimizer would be allowed to remove the call, and then also remove taking address of uninitialized local variable, thus leaving it still in "undefined behaviour" state. You could test this for your compiler by turning up optimizations and verifying from assembly output, that call to foo gets inlined (producing no code), and see if printf crashes then.

hyde
  • 60,639
  • 21
  • 115
  • 176
0

There is no guarantee that using an uninitialised variable will cause your program to crash - it depends what junk data happens to be in the memory location allocated for the variable.

codebox
  • 19,927
  • 9
  • 63
  • 81
0

On an x86 or x86-64, premature optimization prevent the load of value from the variable:

int b;
b -= b; // b = eax - b, not 0

int c;
c ^= c; // c = eax - c, not 0

// eax, whatever happens to be the value in the eax register.
// rax for 64-bit, eax for 32-bit and less using bit mask,
// ax for int is 16 bit compilers.

Undefined behavior could mean anything. For uninitialized variable, it would behave as its type, but the value is unknown and cannot be verified. An uninitialized variable automatically take the value in the "accumulator" (integral: eax, rax; floating point/simd: xmm0, ymm0, zmm0, etc.) and not load it from random access memory (or cache) unless it is volatile. You can initialize the variable by assigning a value to it, given the non-lvalue does not use an uninitialized variable.

Abraham Le
  • 114
  • 3