1

As I learned from this answer, using the address of an uninitialized variable is not undefined behaviour in C. For example, I could write:

#include <stdio.h>

int main(void) {
    
    char letter;
    printf("%p\n", &letter); //prints '0061ff1f'

    return 0;
}

However, if I write the following code:

#include <stdio.h>

int main(void) {
    
    char *letter1;
    printf("%p\n", letter1); //gcc issues warning

    return 0;
}

gcc gives me the following error:

C:\...>gcc -Wall -Wextra -pedantic -std=c11 test.c -o main
test.c: In function 'main':
test.c:12:2: warning: 'letter1' is used uninitialized in this function [-Wuninitialized]
  printf("%p\n", letter1);

If I understood it correctly, an uninitialized pointer could point to any memory address, which is why using it is generally a bad idea. But why does the same thing work with uninitialized variables, i.e. why does an uninitialized variable not point to any memory adress but to a location that we can use safely? Why does the language treat pointer variables and normal variables so differently in this respect?

Moritz Wolff
  • 436
  • 1
  • 7
  • 16
  • What do you mean by "crashes"? With your commands, I can reproduce the warning (along with some others), but I don't get a crash. The first example is the address of an undefined character. The second is an undefined address of a character. – erik258 Aug 02 '21 at 22:58

3 Answers3

5

When you wrote

char letter;
printf("%p\n", &letter);

you declared a variable called letter. It has a well-defined location (or address). The only thing we don't know is which char value is in it -- that's either indeterminate or undefined, depending on who you ask. So if you had tried to do printf("%c\n", letter), that might have gotten you into trouble, because that would try to print the undefined/indeterminate value.

But when you wrote

char *letter1;
printf("%p\n", letter1); //program crashes

that's completely different. letter1 is a variable of type pointer-to-char. As before, it has a well-defined location, and an indeterminate initial value. But what's confusing here is that the value it doesn't have is (or would be) also an address.

If you wrote

printf("%p\n", &letter1);

you'd print the address of letter1, and as I said, that's well-defined. But you tried to print

printf("%p\n", letter1);

and there you try to print the address in letter1, which is a much bigger problem.

(I wouldn't expect an actual crash, though -- in practice I'd merely expect a "random value". I wouldn't expect a crash unless you tried to do printf("%c\n", *letter1).)

One more thing: Taking the address of an uninitialized variable can't be undefined, because plenty of well-defined programs do just that! Taking an address of an uninitialized variable and passing it to a function can be a good way of assigning a value to a variable. If you have a function that returns a value "by reference", you're probably going to pass it the address of a variable, and it will often be uninitialized, like this:

char *p;
int n = strtol("23skidoo", &p, 10);
printf("%d %s\n", n, p);

Footnote: I wrote that the initial value was "either indeterminate or undefined, depending on who you ask", and that alludes to a tremendous subtlety which I only learned about a couple of days ago, which is that the indeterminacy/undefinedness of the initial values of local variables like these can evidently depend on whether they do or might have their addresses taken. There's sort of a Heisenberg -- or maybe Schrödinger -- uncertainty principle here, where the behavior depends on how closely you attempt to observe it. If your program actually did crash when you tried to print the value of letter1, it might not crash if you changed it to printf("%p %p\n", &letter1, letter1);.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • That clears everything up, thank you! To be precise, it didn't crash but gcc issued a warning. – Moritz Wolff Aug 02 '21 at 23:07
  • In order for the Standard to allow an optimization, it *must* classify as Undefined Behavior any action which could cause the effects of any optimizations to be observably inconsistent with sequential program execution, even when possible program behaviors that could result from the intended optimization might be equally acceptable. Allowing a compiler given `struct2=struct1; struct3=stuct1;` to omit writes to members of `struct2` and `struct3` corresponding to uninitialized members of `struct1` would be a useful optimization, but only if unused parts can safely be left uninitialized. – supercat Aug 03 '21 at 15:56
3

In the first case, you are printing the address of letter, not its value.

In the second case, you are attempting to print the value of letter1, which is indeterminate.

A variable has an address whether it contains a valid value or not. letter1 has an address, which you can print with

printf( "%p\n", (void *) &letter1 );

which is the exact same thing you're doing with letter.

John Bode
  • 119,563
  • 19
  • 122
  • 198
3

An uninitialized object has an address. Even when the contents of memory at that address have not been initialized, the object has an address, so its address is a defined value (as long as memory is reserved for the object).

In contrast, the value of an uninitialized pointer is not a defined value.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312