Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find and avoid uninitialised primitive members in C++?

There is a common C++ bug with uninitialised primitive members:

#include <iostream>

class A {
  public:    
    int x;
};

int main() {
  A a;
  std::cout << a.x;
  return 0; 
}

a.x will be uninitialised. I understand why it happens and want to find a solution for catching such bugs. I checked gcc and cppcheck, they don't report these members.

EDIT Checked gcc with flags -Wall -Wextra -Werror -pedantic -Wold-style-cast -Wconversion -Wsign-conversion -Wunreachable-code

The first version of gcc that detects a bug is 5.1. g++-4.9 doesn't detect it, clang++-3.6 fails too.

like image 341
Artem Verkhoglyadov Avatar asked Jul 15 '15 16:07

Artem Verkhoglyadov


2 Answers

Yes they do… sort of:

main.cpp: In function 'int main()':
main.cpp:10:18: warning: 'a.A::x' is used uninitialized in this function [-Wuninitialized]
   std::cout << a.x;
                  ^
0

In the above linked example, I'm using GCC 5.1 trunk with -Wall.

Turn on more GCC warnings and/or upgrade.

Also tested, and found not to warn:

  • GCC 4.4.7
  • GCC 4.9.2 (by RiaD)
  • Clang 3.6.0

Honestly I'm not sure what else you can do. You could create a tool to do this, but then you'd be creating a compiler or static analyser. :)

So, I guess, just hope the people who know how to do that catch up…

like image 160
Lightness Races in Orbit Avatar answered Nov 13 '22 15:11

Lightness Races in Orbit


This bug is diagnosed by valgrind with the (default) tool memcheck, producing a number of warnings, including:

$ valgrind ./unin
…
==12185== Use of uninitialised value of size 8 
==12185==    at 0x4F39BC3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185==    by 0x4F3AD89: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185==    by 0x4F3AF8C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185==    by 0x4F474E9: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)             
==12185==    by 0x400763: main (in [censored]/unin)

It should also be found by Clang's address sanitizer. However, I realize you are most interested in a compile-time check. After all, the existing test suites may never execute some of the code, and catching a bug sooner is always better. You could use GCC 5.1 (even if you used it only for this purpose), or you could use a dedicated static analyzer. Fortunately, clang comes with a static analyzer invoked as scan-build (included in at least the Debian/Ubuntu packages):

$ scan-build clang -c unin.cxx 
scan-build: Using '/usr/lib/llvm-3.6/bin/clang' for static analysis
unin.cxx:10:3: warning: Function call argument is an uninitialized value
  std::cout << a.x;
  ^~~~~~~~~~~~~~~~
1 warning generated.
scan-build: 1 bug found.
like image 25
Arne Vogel Avatar answered Nov 13 '22 16:11

Arne Vogel