6

According to this question std::array is allocated on the stack. However when using it together with Valgrind it shows me a heap allocation, even for elements which are allocated on the stack. Is this a false positive or real?

Here follow two mwe to illustrate the behavior.

No heap:

The following code:

#include <array>

int main() {
    std::array<int*, 1> map;
    int value = 0;
}

Produces the expected following Valgrind output:

==14425== HEAP SUMMARY:
==14425==     in use at exit: 0 bytes in 0 blocks
==14425==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated

With heap:

However if I try this code:

#include <array>

int main() {
    std::array<int*, 1> map;
    int value = 0;

    map.at(0) = &value;
}

Valgrind gives me

==14539== HEAP SUMMARY:
==14539==     in use at exit: 72,704 bytes in 1 blocks
==14539==   total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==14539== 
==14539== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==14539==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14539==    by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==14539==    by 0x40106B9: call_init.part.0 (dl-init.c:72)
==14539==    by 0x40107CA: call_init (dl-init.c:30)
==14539==    by 0x40107CA: _dl_init (dl-init.c:120)
==14539==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==14539== 

Added compile settings:

g++ -std=c++11 -O0 valgrind.cpp -o valgrind_build -I ../fake -I ../src
valgrind --track-origins=yes --dsymutil=yes --leak-check=full --show-leak-kinds=all ./valgrind_build

valgrind --version
valgrind-3.11.0

g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
magu_
  • 4,766
  • 3
  • 45
  • 79
  • 2
    What compiler and what flags? All this should be removed in an optimized build anyway. – Matthieu Brucher Feb 13 '19 at 17:47
  • 1
    could be a false positive, its hard to come up with a usage of this snippet that wont leave you with a dangling pointer in the array (or using heap allocation) – 463035818_is_not_an_ai Feb 13 '19 at 17:48
  • 10
    "*According to this question std::array is allocated on the stack.*" No, that's not what it says. What it says is that `std::array` is not allowed to perform heap allocation. If you allocate `array` *itself* on the heap, then the array will be on the heap. – Nicol Bolas Feb 13 '19 at 17:48
  • 1
    Your second example look like what you'd see from the global object in the standard library (like `cin` and `cout`). – NathanOliver Feb 13 '19 at 17:48
  • very related: https://stackoverflow.com/questions/31775034/valgrind-error-in-use-at-exit-72-704-bytes-c-initialization-list-weirdness-w – NathanOliver Feb 13 '19 at 17:52

1 Answers1

10

The code

map.at(0) = &value;

introduces bounds checking, which might in turn need to use stuff allocated dynamically (e.g. from the <iostream> library).

You may try again with

map[0] = &value;

which doesn't apply bound checks.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Wow, didn't think about bound checking introducing dynamic allocation. But you are correct. – magu_ Feb 13 '19 at 17:53
  • 1
    Yet another reason to not automatically use `at()`. – Pete Becker Feb 13 '19 at 18:19
  • The only dynamically allocated memory I could see `at()` using is when throwing a `std::out_of_range` exception. If you don't go out of bounds, no exception is thrown. So, what else could be using dynamic allocation in this case? Maybe some kind of static initialization even if the exception is not thrown? – Remy Lebeau Feb 13 '19 at 19:43
  • 1
    @remy _"Maybe some kind of static initialization even if the exception is not thrown?"_ Sure, that is what I was thinking of. – πάντα ῥεῖ Feb 13 '19 at 19:48