1

I made a small test to see how the stack is used in a C application.

#include <stdio.h> void a(void) { int a = 0; } void b(void) { int b; printf("%i\n", b++); } int main(void) { a(); b(); b(); b(); fflush(stdin), getc(stdin); return 0; } 

Isn't b allocated in the same place on the stack where a was? I would expect the output to be 0 1 2, but instead I get the same garbage value three times. Why is that?

2
  • 5
    Disassemble your program and look at what it's actually doing, not at what you think it's doing. Commented Oct 26, 2011 at 20:31
  • 3
    This is officially undefined behavior (using an uninitialized variable), and there is no guarantee that the compiler will allocate memory in a consistent location, or even allocate any memory at all. The local variable b was probably enregistered. Commented Oct 26, 2011 at 20:34

6 Answers 6

10

About the only way to get a definitive answer about why you're getting what you're getting is to get the assembly language output from the compiler and see what it's doing. At a guess, the entirety of a() is being removed as dead code, and b is probably being allocated in a register, so even if a had been allocated and initialized, there'd still be a fair chance that b wouldn't end up in the same storage.

From a viewpoint of the language, there's not really any answer -- your code simply has undefined behavior from using an uninitialized variable. Just to add insult to injury, your fflush(stdin) also causes undefined behavior, so even if the rest of the code made some sense, you still wouldn't have any guarantee about what output it would produce.

Sign up to request clarification or add additional context in comments.

3 Comments

I don't think using an unitialized variable is per se UB, only if the trash value that happens to be in there is a trap representation for the underlying type.
@JerryCoffin, the difference is that UB is allowed to eat your harddisk and spend all your money. For an indeterminate value the compiler is just allowed to give you any bit pattern of his liking, and not to crash on you, e.g.
@JensGustedt: The committee disagrees. See Defect Report #109: "Use of an indeterminate valued object results in undefined behavior."
3

I might guess that you got all zeroes as output, but this is not necessarily the case. In function b, you are declaring a new int b which is created for each execution of the code. B is uninitialized in your code, but some compilers will zero this value. This is not standard, and should NOT be counted on. You should always initialize your variables.

As far as the stack goes, that is implementation specific and dependent upon the compiler, optimizer settings, etc. There is no guarantee that this variable ever lives on the stack. Chances are it does not, given the short duration of scope it may just live in a CPU register.

In the above code b and a are completely independent variables, and thus should not be counted on to have the same value, even if they are stored in the same memory location.

2 Comments

I picked your answer for this bit, "given the short duration of scope it may just live in a CPU register".
Side warning: Some compilers may deliberately treate uninitialized variables differently when compiling in debug mode (mostly to make debugging easier).
3

What you are doing is invoking undefined behaviour, except in C99 where the value is indeterminate. Either way you don't know exactly what will happen.

There are no guarantees about the condition of the stack when you leave a method, nor what value uninitialized variables will have.

5 Comments

No, this is not UB the value is just indeterminate. This is only UB when the value that happens to be in the variable is a trap representation for the type, which most architectures don't have for int these days. The compiler is not free to do anything he wants, but just to put any bit pattern of its liking in the variable, but not to eat up the hard disk.
@Jens Gustedt: When I read the C standard I see this: "Undefined behavior: The value of an object with automatic storage duration is used while it is indeterminate. (6.2.4, 6.7.8, 6.8.2)." According to this definition, why is it not undefined behaviour in this case? Am I misunderstanding something?
@Jens While you two are on this topic, may I also point out this interesting note in case you haven't seen it: open-std.org/jtc1/sc22/wg14/www/docs/dr_338.htm
@PascalCuoq, yes, interesting. I checked in C1x and the change actually made it in, there. So now thing are getting even more weird. In C89 b() has UB, in C99 it hasn't. For C1x it will be UB unless the address of the variable is taken somewhere, even just by putting a (void)&b; somewhere. Then the situation would be again as in C99, indeterminate value.
@MarkByers, you are citing from Annex J, aren't you. This part of the standard is just informative, not normative. The fact that indeterminate values lead to UB was voluntarily excluded from C99, compared to C89. E.g in C99 the standard explicitly allows to read all the bytes of an uninitialized object through a unsigned char[]. Unsigned char never can have trap representations, and thus you can safely inspect the bit pattern that is locate at the address of the object.
2

The assignement that is part of b++ in your function b() must not necessarily be performed by the compiler since b is not read afterwards. But what is more important here is if you don't have an initializer:

The initial value of the object is indeterminate. 

that's it. (Not UB, as other say.) The compiler is free to implement this in any way of his liking.

NB: The word "stack" doesn't appear anywhere in the C standard. Whereas this a convenient concept to implement auto variables in C, there is no obligation for the compiler to use that concept for a given variable, and in particular there is no obligation at all to store a variable in memory. It can well just hold all variables in registers, if the platform allows for that. So if you'd look into the assembler that is produced for a() you most probably just see nothing, just an empty return.

Comments

1

Have you read these slides already?

http://www.slideshare.net/olvemaudal/deep-c

There is some discussion about stack behaviour like this. Of course you can never rely on the value in an auto variable. The compiler is free to put these variables on registers. Or on the stack.

2 Comments

+1 Although this doesn't directly answer the question, those are some great slides!
Give "Expert C Programming: Deep C Secrets" a read if you like the slides
1

B's value is indeterminate. You can't learn anything by running this program.

2 Comments

The proper adjective to describe the contents of b is "indeterminate", as noted in Jens Gustedt's answer and comments.
No, the value is indeterminate which is a different concept in the C standard.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.