0

Consider

#include <iostream>
#include <cstdio>

using namespace std;

struct Foo {
    FILE* fp;
    Foo* mp;
};

int main()
{
    Foo f1;   // default initialization
    cout << f1.fp << '\t' << f1.mp << '\n';

    Foo f2{}; // zero initialization / direct-list-initialization
    //Foo f2 = {}; // zero initialization / copy-list-initialization
    cout << f2.fp << '\t' << f2.mp << '\n';

    FILE* fp; // local POD variable, undefined
    cout << fp << '\n';
}

Compilation and execution,

g++ tp.cc && ./a.out

gives something like:

0       0x5639eb8f00c0
0       0
0x5639eb8f02f0

Both FILE* fp and Foo* mp are of pointer types, thus also of POD types. So, in the default initialization (line Foo f1;), both should be not initialized. But the result, however, appears to suggest the FILE* fp is zero-initialized. How come? Is FILE* a special pointer type that the compiler loves to (zero) initialize, but refuses to zero initialize other pointers?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark Taylor
  • 117
  • 8
  • 2
    I think it might just be a coincidence, here's my godbolt experiment [output](https://godbolt.org/z/WxKf6o4TW) – user8510613 May 11 '22 at 09:30
  • 4
    It's not deterministic. Ex: Change nothing else in your code except [flip the order](https://godbolt.org/z/q449hzo7P) of those fields in the structure, and run it again. Those are *indeterminate* with default initialization. – WhozCraig May 11 '22 at 09:30
  • @user8510613 clang seems to invert the result – Mark Taylor May 11 '22 at 09:36
  • @WhozCraig Yeah, it also has something to do with the orders of how we place these two fields. Creepy compiler features :^) – Mark Taylor May 11 '22 at 09:38
  • So, it has nothing to do with `FILE*` pointers, just compiler policies on pointer fields, which is somewhat weird, though. – Mark Taylor May 11 '22 at 09:43
  • 1
    They are just random values, `0` is just as random as `0x5639eb8f00c0`. – BoP May 11 '22 at 10:42
  • Those pointers are allocated on the stack, and depending on how the corresponding stack memory was allocated before, you'll see diferent results :) That also explains why, when inverting the variables in your code, you also invert the initialized content – MamorukunBE May 11 '22 at 10:57
  • @MarkTaylor: It's not "compiler policies on pointer fields" specifically. The compiler treats them like any other POD type, and does nothing with them, so they have whatever garbage was in that memory before it was repurposed to store an instance of your `struct`. You could have used `uintptr_t` fields there and you'd get the same result (if you made sure they were printed as hex). Or not, since in theory the values on the stack might change from run to run. The compiler chose to do *nothing*, and you got undefined values, that's all. – ShadowRanger May 11 '22 at 13:29
  • Default-initialization of `Foo` does _not_ initialize its members at all. Both `f1.fp` and `f1.mp` will have _indeterminate values_. Reading these values in `cout << f1.fp << '\t' << f1.mp << '\n';` causes undefined behavior. The same applies to `cout << fp << '\n';`. Any output is valid under UB. – user17732522 May 11 '22 at 13:31

1 Answers1

0

You didn't initialize the pointers. They get random (undefined) values (specifically, whatever the runtime happened to leave on the stack during initialization before main was called). Sometimes those values happen to be NULL. C++ didn't initialize them, you just got "lucky". As noted in the comments on your question, on different compilers, or with different variable declaration orders, the FILE* often ends up non-NULL, because it's not being initialized, just inheriting whatever happened to already be on the stack.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Yeah, they are just some random values on the stack. POD (`char`, `int`, `float`, `pointer`, etc.) type fields don't get initialized on default initialization if the constructor doesn't initialize them. – Mark Taylor May 12 '22 at 08:14