Why are is the address of a pointer different when accessed in two different ways?

Advertisements

Why does the following code output two different pointer addresses for one of the Timer._d objects in the vector? This is a minimal example of the issue I’m seeing — see below for a complete picture.

#include <cstdio>
#include <vector>
#include <iostream>

struct Data {
  int duration = 0;
};

class Timer {
  private:
    Data _d;
  public:
    Timer () {}
    void setTime(int duration) {
      _d.duration = duration;
      char x[100];
      sprintf(x, "Pointer to _d in setTime: %p", (void*)&_d);
      std::cout << x << std::endl;
    }

    const Data* getDataPtr() {
     return &_d;
    }
};


int main() {

  std::vector<Timer> vec;

  vec.emplace_back(Timer());
  vec.back().setTime(1);

  vec.emplace_back(Timer());
  vec.back().setTime(2);

  for(Timer &timer : vec) {
      char x[100];
      sprintf(x, "Pointer to _d in main: %p", (void*)timer.getDataPtr());
      std::cout << x << std::endl;
  }
}

Output is:

Pointer to _d in setTime: 0x7fffbfe52e70 <-- Different inside/outside
Pointer to _d in setTime: 0x7fffbfe53ea4 <-- Same inside/outside
Pointer to _d in main: 0x7fffbfe53ea0
Pointer to _d in main: 0x7fffbfe53ea4

It seems like the last pointer always points to the "correct" address, but all of the previous ones do not. I’m trying to modify the data in the Data structure but it gets put in a different place for all of the elements in the vector except for the last one.

The reason I’m asking is because in my real application, I need to pass a pointer to _d to a separate function that passes it as an argument to a callback function (from an external library, Countimer). For example:

...
class Timer : public Countimer {
  private:
    Data _d;
  public:
    Timer () {}
    void setTime(int duration, timer_callback cb) {
      _d.duration = duration;
      setCounter(duration, cb, (void *)&_d); // Inherited from Countimer, sets a timer in minutes that calls cb and passes the pointer to it at the end of the timer
    }

    const Data* getDataPtr() {
     return &_d;
    }

    // Toy function to update something in _d
    void setDuration(int duration) {
      _d.duration = duration;
    }
};

void timer_complete(void *data) {
  Data *ptr = (Data *)data;
  // do something with the pointer, animate a screen or whatever
}

...

vec.emplace_back(Timer())
vec.back().setTime(1, timer_complete);

...

for(Timer &timer : vec) {
  // Update something in the Data struct so our callback can use it later:
  timer.setDuration(3); // Toy example
}

I’d like to have one container of Timer‘s (inside the vector) and have them store their own data in a way that can be passed to an external callback. However, the pointer passed to timer_callback is the first one (e.g. 0x7fffbfe52e70) and so the modifications aren’t getting picked up when the timer is completed (except for the Timer added last). It’s possible this is an XY problem and there’s something I should be doing differently overall, but I believe passing a pointer to some user data to a callback is a fairly common practice and I’m just trying to have everything contained neatly so I don’t need to keep separate pools of Data and Timer and then match them up later.

>Solution :

vec.emplace_back(Timer());

A std::vector holds all its contents in contiguous memory. That’s the whole purpose of having a std::vector: this unique characteristic that no other container in the C++ library has.

To do that, std::vector allocates a single contiguous memory block in advance, and then when it grows in size the pre-allocated memory is consumed for that purpose.

And when the vector’s growth reaches the internal limit, the next attempt to grow the vector causes reallocation. A new, larger memory block gets allocated, and the existing contents of the vector get moved/copied to it.

This is the behavior you are seeing. Some of the pointers the shown code prints occurs before the internal vector’s contents get reallocated, and some after it gets reallocated by one of the emplace_back calls.

Leave a ReplyCancel reply