3
#include <stdio.h>

int main(void) {
    int i = 0;
    printf("abc %n %d", &i, i);
    printf("\n%d\n", i);
}

When I executed that, I got the following result.

abc  0
4

I thought that this result is intended.

But when I execute next one, I got a different result.

int main(void) {
    int i; // not initialize
    printf("abc %n %d", &i, i);
    printf("\n%d\n", i);
}

which produced:

abc  1
4

I don't know why the result of i in the first printf() is 1.

Even, I found more stranger behavior:

int main(void) {
    int sdf;
    printf("abc %n %d", &sdf, sdf);
    printf("\n%d\n", sdf);
    int i;
    printf("abc %n %d", &i, i);
    printf("\n%d\n", i);
}

with this output:

abc  1
4
abc  Random_Value
4

The first one always shows 1 but others show random value (I think this is garbage value).

I think garbage value was intended but I didn't get why the first one has a different result.

dbush
  • 205,898
  • 23
  • 218
  • 273

5 Answers5

3

The result is well defined. Address of i and i are passed to the printf. Then printf assigns the value to i. But because i was passed before it, printf will print the value of i before the call.

The latter snippets use uninitialised variable and this undetermined value will be printed first. The behaviour later will be the same as per snipped 1.

The uninitialised variable will have undetermined value. It can be also an UB if the the type of the variable has trap representation (which is not the case here) But because the executing environment is the same, it is very likely that you will see the same values. If you run it on other OS, other computer or will use other compiler they will be probably different.

0___________
  • 60,014
  • 4
  • 34
  • 74
3

The value of an uninitialized local integer variable of storage class auto, that is stored in the stack area, is undefined. For this reason, printing it, a random value is absolutely expected.

That's why, in your output

abc  1
4
abc  Random_Value
4

the value 1 is garbage as well.

In fact it is the first location in the stack, and its value could be different changing system and/or compiler. My guess is that its value is 1 because it represents a "ghost argc" that, being main a very special function, is present even if the function is defined without parameters, and its value is 1.

Since argc represents the number of parameters used to call your program from command line (at least 1: the executable name) there's a way to verify this hypotesis: call your program in this way

executableName foo

This would make argc become 2, so the value shown by that first printf should become 2 as well.

Out of curiosity, I tested this hypotesis by compiling your second example on my machine (DevC++ with gcc compiler under W10 64bit OS). I confirmed two statements:

  1. When undefined behavior occurs, changing environment leads to a change in the output
  2. argc is present in the stack and affects the initial value of the uninitialized local variables

Output after executing uninitVars.exe

abc
0
abc
1

Output after executing uninitVars.exe dog

abc
0
abc
2

Output after executing uninitVars.exe dog cat

abc
0
abc
3

So it seems that the first four bytes of my stack are always set to 0 (are they the location of the return value?) and that the second one is actually argc, even if it is not explicitly defined in the main() prototype.


The second print, instead, shows different values because it is defined after two printf calls, and their execution write several bytes in the stack (some of them are addresses, and that explains why the value is always different, as the addresses of a process are virtual and always different).

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
  • I think this is why problem happen. But the first stack of the main function without parameter always has 1, not more than 2. I tried to "executableName foo" but it just printed '1'. – Kyung Kyu Lee Jun 28 '20 at 14:03
  • @KyungKyuLee this denies my _clever guess_. But it must be anyway something written by the os just before passing the execution to main.. I cannot be sure about it, sorry – Roberto Caboni Jun 28 '20 at 14:08
  • @KyungKyuLee Have a look to my edit. I've performed a test on my PC and I think that, even if I don't reproduce your behavior, I've found out something interesting. Useful if you still want to "play" with the stack. (but in your **real** programs the best answer remains: initialize variables before reading their values! ;) ). – Roberto Caboni Jun 28 '20 at 14:55
2

Is printf(“%n %d”, &a, a) defined well in printf?

If a has type int (equivalently, signed int) and is not const-qualified then yes, otherwise, no. In the well-defined case, the value of a at the time of the call is printed, and after the call returns, a will have the value 0.

Perhaps the key points here are that the arguments to a function call are evaluated before the called function is entered, and they are passed by value. There is a sequence point between the evaluation of the arguments and execution of the first statement in the function body, so the fact that a is read by the calling function and written by printf does not present any particular issue.

[...] when I execute next one, I got a different result.

int main(void)
{
  int i; // not initialize
  printf("abc %n %d", &i, i);
  printf("\n%d\n", i);
}
abc  1
4

I don't know why the result of 'i' in the first printf() is 1.

No one does. The value of an automatic variable that has neither been initialized nor assigned to is indeterminate. The same considerations apply here as in the title question: The expression i in printf's argument list is evaluated before the any part of printf executes, so the fact that printf will later assign a value to it does not affect the value it receives.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Re “Reading the indeterminate value of a local variable that has neither been initialized nor assigned has undefined behavior”: No, it does not. If the type has a trap representation, then reading an indeterminate value could cause a trap for that reason. Or if its address had not been taken, reading an automatic uninitialized object has undefined behavior due to C 6.3.2.1 2. But if the address is taken, reading an indeterminate value is not by itself undefined behavior. – Eric Postpischil Jun 28 '20 at 13:45
  • @EricPostpischil, according to annex J.2, yes it does: "The behavior is undefined in the following circumstances: [...] The value of an object with automatic storage duration is used while it is indeterminate." However, I acknowledge that annex J is only informative, and I'm inclined to think that its claim is probably drawn, with some loss of precision, from 6.3.2.1.2. – John Bollinger Jun 28 '20 at 13:58
  • Updated to address the undefinedness claim and to make wording more precise regarding variable classification. – John Bollinger Jun 28 '20 at 14:02
  • All of the compilers I have tested--clang, gcc, icc, msvc--treat that statement from J.2 as normative and also as though the "with automatic storage duration" qualifier were not present. I expect that if you reported this as a bug you would be told that, to whatever extent the normative text does not support this, the standard is defective. – zwol Jun 28 '20 at 16:28
  • @zwol: How did you test how the compiler treated a statement in the standard? – Eric Postpischil Jun 28 '20 at 19:02
  • @EricPostpischil Read an indeterminate value conditionally, and see if the compiler assumes that that branch is unreachable. This assumption is allowed by the standard if the behavior is full-on undefined, not if it is merely unspecified. – zwol Jun 28 '20 at 21:59
2

With the variable in question not being initialized, the behavior is at best unspecified and at worst undefined.

The local variables i and sdf are uninitialized, which means that their values are indeterminate. The formal definition is in section 3.19 of the C standard and is as follows:

3.19.2

1 indeterminate value

either an unspecified value or a trap representation

3.19.3

1 unspecified value

valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance

2 NOTE An unspecified value cannot be a trap representation.

3.19.4

1 trap representation

an object representation that need not represent a value of the object type

This basically means that the value is unpredictable. In fact simply reading an indeterminate value can in some cases lead to undefined behavior. This can happen if the indeterminate value happens to be a trap representation as defined as above.

It can also be undefined behavior if the variable in question never had its address taken, however that doesn't apply in this case because you did take the address. This behavior is documented in section 6.3.2.1p2:

Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined. If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.

So assuming your implementation doesn't have trap representations, the values of sdf and i are unspecified, which means they could be any value, including 0 or 1. As an example, you get the values 1 and (some random value) for sdf and i. When I run the same code I get this:

abc  0
4
abc  0
4

And if I compile with -O3 which sets a higher optimization level, I get this:

abc  1446280512
4
abc  0
4

As you can see, running the came code as you that reads an unspecified value can have different results on different machines, or even on the same machine with different compiler settings.

There's nothing special about the value 0 that I got or the value 1 that you got. They're just as random as 1446280512.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

First of all, you can not print the result of %n in the same line, i will keep its previous value. About the random value, while you have not initialized the i and sdf, they have a random value (most likely 0 but there is no guaranty). In C, global variables will have 0 value if they are not initialized, but the local variables will have uninitialized values (random).

Amir Fakhim Babaei
  • 1,014
  • 1
  • 5
  • 13