3

I am playing with C++ value initialization.
Therefore, I am printing uninitialized values in order to highlight (un)initialization depending on C++ standard version.
But uninitialized values convey often the zero value :-(

How to highlight if a variable has not been initialized?
(i.e., How to say to the compiler to use a specific value for uninitialized variables?)

Maybe this could be done using a magic user-provided allocator...


EDIT: My below snippets is not for production. I just wanted to check the implementation (by the compiler) of the C++ Standard about uninitialization/value-initialization/zero-initialization/default-initialization mechanisms. I do not want compiler warnings about uninitialized variables. I do not want to use MemoryCheckers. I just want to highlight that compiler zero-initializes some variables, default-initializes some other variables and does not initialize at all other variables. And this initialization behavior also depends on the version of C++ Standard. If you think the best way is to use compilers warnings or MemoryCheckers, please provide an answer using the below snippets.


Please do not read below detailed attempts if you have already understood my question.


You can build/run my first snippet on Coliru

#include <iostream>

struct A  {
  A() {} // user-defined default ctor does not initialize i
  int i;
};

struct B {
  A a;
};

int main()
{
  std::cout << B().a.i << '\n';
    // Results: C++03 -> uninitialized
    //          C++11 -> zero-initialized

  std::cout << B{}.a.i << '\n';
    // Results: C++03 -> Do not compile - B{} is correct since C++11
    //          C++11 -> uninitialized because
    //          DR1301 defines B{} as an aggregate-initialization
}   //          => A is value-initialized using the user-defined ctor

When compiled using -std=c++03 the execute may probably prints a zero value but I would prefer a non-zero value.

0

Possible output using -std=c++11

0
1208617840

Another possible output using -std=c++11

0
-201855824

But my more advanced snippet has now zero-values for uninitialized object B{}.a.i :-(

#include <iostream>

struct A {
  A() {} // user-defined ctor does not initialize i
  int i;
};

struct B  {
  A a;
};

int main()
{
  std::cout <<"-----------"<< __cplusplus <<"-----------" "\n";
  std::cout <<"B().a.i            = "<<         B().a.i <<'\n';
  std::cout <<"B{}.a.i            = "<<         B{}.a.i <<'\n';

  int d;
  d = 42;
  std::cout <<"(new(&d) B  )->a.i = "<< (new(&d) B  )->a.i <<'\n';
  d = 42;
  std::cout <<"(new(&d) B())->a.i = "<< (new(&d) B())->a.i <<'\n';
  d = 42;
  std::cout <<"(new(&d) B{})->a.i = "<< (new(&d) B{})->a.i <<'\n';
}

Build/Run output:

> g++ -std=c++03 -Wall -Wextra -pedantic main.cpp && ./a.out
-----------199711-----------
B().a.i            = 0
(new(&d) B  )->a.i = 42
(new(&d) B())->a.i = 0

> g++ -std=c++11 -Wall -Wextra -pedantic main.cpp && ./a.out
-----------201103-----------
B().a.i            = 0
B{}.a.i            = 0
(new(&d) B  )->a.i = 42
(new(&d) B())->a.i = 0
(new(&d) B{})->a.i = 0

> g++ -std=c++14 -Wall -Wextra -pedantic main.cpp && ./a.out
-----------201402-----------
B().a.i            = 0
B{}.a.i            = 0
(new(&d) B  )->a.i = 42
(new(&d) B())->a.i = 0
(new(&d) B{})->a.i = 0
oHo
  • 51,447
  • 27
  • 165
  • 200
  • 2
    There's no way to distinguish an uninitialized variable during run-time. – 101010 Oct 09 '15 at 13:54
  • 2
    Maybe you want something like Clang's [MemorySanitizer](http://clang.llvm.org/docs/MemorySanitizer.html)? – TartanLlama Oct 09 '15 at 13:55
  • 2
    Some compilers have debug options for setting uninitialized memory to special values, but it depends on which compiler you're using. – Dan Hulme Oct 09 '15 at 13:57
  • Hi @DanHulme I can use g++ and clang++. – oHo Oct 09 '15 at 13:58
  • 2
    Compilers may use specific values for uninitialized values when building in debug mode. MSVC is known to do that to allow developpers to easily detect uninitialized memory when running a program through the debugger. But it is an implementation detail, and anyway, I really do not know how to control it, not how to do the same with other compilers. – Serge Ballesta Oct 09 '15 at 13:59
  • 2
    Keep in mind that you're playing with a Heisenbug here. Since the reading of uninitialized variables is Undefined Behavior, and GCC is known to optimize based on an assumption of well-defined behavior including well-defined use of variables, the mere attempt to use _directly_ use an uninitialized variable may affect that variable, and others as well. In particular, GCC is known to outright remove code that touches uninitialized variables. – MSalters Oct 09 '15 at 14:33
  • 2
    Does `g++ -Wuninitialized` not help you out? – Mark B Oct 09 '15 at 14:46
  • Relevant for gcc [Why does GCC only sometimes detect the use of a variable before its initialization?](http://stackoverflow.com/a/29314038/1708801) note the quote for the gcc wiki: *Second, the set of false positives or negatives varies according to the optimisations enabled. This also causes high variability of the warnings reported when optimisations are added or modified between releases* which backs up MSalters comment. – Shafik Yaghmour Oct 09 '15 at 14:58
  • @MarkB see my comment on `-Wuninitialized` – Shafik Yaghmour Oct 09 '15 at 15:06
  • Hi @MarkB I do not want the compiler warns about uninitialized variables. I just want to highlight that compiler zero-initializes some variables, default-initializes some other variables and does not initialize at all the rest (this can also depend on the version of C++ Standard). – oHo Oct 09 '15 at 15:09
  • "I do not want the compiler warns about uninitialized variables". Why? This is exactly the thing you should be using, all the time. "I do not want to do the sane thing I want to solve my problem some other way". First do the sane thing, then see if you still have a problem. – Ben Oct 09 '15 at 16:37
  • Hi @Ben. Yes this snippet is not for production. It is just to check the implementation by the compiler of the C++ Standard about value-initialization / default-initialization / uninitialization mechanisms. – oHo Oct 09 '15 at 16:49

1 Answers1

0

One possible solution is to use clang++ instead of g++.

clang++ v3.7 has highlighted uninitialized values in my specific case.

Note: This answer is based on C++ value initialization that has changed since C++11. Value initialization is when parentheses/braces are empty: T(); T{}; new T(); new T{};

Build/Run below snippet on Coliru

#include <iostream>

struct A
{
  A() {} // ctor does not initialize 'i'
  int i;
};

struct B // implicit ctor
{
  A a;
  int i;
  void set() { a.i = i = 42; }
};

std::ostream& operator<< (std::ostream& os, const B& b)
{  os <<'\t'<< b.a.i <<'\t'<< b.i;  return os; }

int main()
{
  std::cout <<"----------"<< __cplusplus <<"----------" "\n";

  B b; // used to reset memory for 'placement new'

  b.set(); std::cout <<"new(&b)B   "<< *new(&b)B   <<'\n'; // All uninitialized (in all C++ standards)

  std::cout          <<"       B() "<< B()         <<'\n'; // B::A::i uninitialized in C++03, zero-initialized in C++11
  b.set(); std::cout <<"new(&b)B() "<< *new(&b)B() <<'\n'; // B::i zero-initialized (in all C++ standards)

#if __cplusplus > 2011*100                                 // B{} is aggregate-initialization (DR1301)
  std::cout          <<"       B{} "<< B{}         <<'\n'; // => B::A::i value-initialized
  b.set(); std::cout <<"new(&b)B{} "<< *new(&b)B{} <<'\n'; // => B::i     zero-initialized
#endif
}

Build output & Possible run output

> clang++ --version
clang version 3.7.0 (tags/RELEASE_370/final 246979)
Target: x86_64-unknown-linux-gnu
Thread model: posix

> clang++ -std=c++03 -Wall -Wextra -pedantic main.cpp && ./a.out
----------199711----------
new(&b)B    42      42
       B()  0       0
new(&b)B()  0       0

> clang++ -std=c++11 -Wall -Wextra -pedantic main.cpp && ./a.out
----------201103----------
new(&b)B    42      42
       B()  0       0
new(&b)B()  0       0
       B{}  4196348 0
new(&b)B{}  42      0

> clang++ -std=c++14 -Wall -Wextra -pedantic main.cpp && ./a.out
----------201402----------
new(&b)B    42      42
       B()  0       0
new(&b)B()  0       0
       B{}  4196348 0
new(&b)B{}  42      0

> clang++ -std=c++1z -Wall -Wextra -pedantic main.cpp && ./a.out    
----------201406----------
new(&b)B    42      42
       B()  0       0
new(&b)B()  0       0
       B{}  4196348 0
new(&b)B{}  42      0
oHo
  • 51,447
  • 27
  • 165
  • 200