2

Is it possible to get a reference to an object if I have only the address of a member variable of that object?

struct example {
    int var;
};

int main() {
    example exampleObject;
    int* point = &exampleObject.var;

    // can i get objPointer to point to exampleObject only using point
    example* objPointer;
}
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
Faust
  • 89
  • 5
  • Well yes can I get address of that object using only address of variable of object? – Faust Jul 25 '20 at 22:58
  • 1
    No you can't using only `point`, at least without falling in some Undefined Behavior trap – Alberto Sinigaglia Jul 25 '20 at 22:59
  • 1
    I think no you can't unless you construct the member var in a special way to have a pointer to the object (this). – asmmo Jul 25 '20 at 22:59
  • 2
    See [Using offsetof to get owner object from member variable](https://stackoverflow.com/questions/34511712/using-offsetof-to-get-owner-object-from-member-variable), [Standard way to find base address of struct from a member](https://stackoverflow.com/questions/21909837/standard-way-to-find-base-address-of-struct-from-a-member). That said, it's not something you would normally do, and if you *have* to do it then perhaps a different design would have made more sense. – dxiv Jul 25 '20 at 23:02
  • I thought i cant do something like that just wanted to check if there is a way with memory or some other library. Thanks for the answer! – Faust Jul 25 '20 at 23:03
  • 1
    You can't do it without assuming that `point` is the address of a member of an `example`, and using knowledge of how `example` is constructed. For example, `example *objPointer = (example *)((char *)(&point) - offsetof(example, var))` or `example *objPointer = reinterpret_cast(reinterpret_cast(&point) - offsetof(example, var))`. The behaviour will be undefined if `point` points at something that is not actually a member of an instance of `example`, and there is no general way to test for that that doesn't involve undefined behaviour. – Peter Jul 25 '20 at 23:06
  • 2
    @Peter you need to use `((char*)point)` or `reinterpret_cast(point)` (`point` rather than `&point`). You want the address that `point` is pointing at, not the address of `point` itself. – Remy Lebeau Jul 25 '20 at 23:08
  • @RemyLebeau - Yes, true. Thanks for the correction. – Peter Jul 25 '20 at 23:25

2 Answers2

3

Potentially yes, but there are limitations.

Only if the class in question is a standard layout class is this possible. In that case, you can reinterpret a pointer to the first member as a pointer to the class itself. You can then indirect through the pointer to get a reference:

static_assert(std::is_standard_layout_v<example>);
example* objPointer = reinterpret_cast<example*>(point);
assert(objPointer == &exampleObject); // guaranteed to pass

Getting pointer to a standard layout class based on a member other than the first may be possible using the offsetof macro, although all attempts to implement that idea that I've seen rely on something that is either technically undefined behaviour, or at least implementation defined.

Here is the implementation from the Linux kernel (not written in C++, but C, which may be more relaxed in what is allowed):

#define container_of(ptr, type, member) ({            \
  const typeof(((type *)0)->member) * __mptr = (ptr); \
  (type *)((char *)__mptr - offsetof(type, member)); })

typeof is not in standard C++, nor even in standard C, but is a GNU language extension. This also uses a statement expression (not to be confused with expression statements), which is another GNU extension.

user3840170
  • 26,597
  • 4
  • 30
  • 62
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I think it's safe to say that if an implementation provides `offsetof()` then it's an opaque thing that is defined. Whether the implementation under the hood invokes UB according to the standard is irrelevant as the implementation knows how it works and can do things that would be UB elsewhere. Put another way, implementation-provided things are black boxes and if the implementation says it's defined, it is _for that implementation._ – cdhowie Jul 25 '20 at 23:22
  • 1
    For example, GCC and LLVM both expand the macro as `__builtin_offsetof` with arguments forwarded. Whatever that thing happens to be, we can assume that it does the right thing if the arguments make sense. – cdhowie Jul 25 '20 at 23:22
  • @cdhowie All implementations are required to provide `offsetof`. `Whether the implementation under the hood invokes UB according to the standard is irrelevant` Correct. Regardless, `offsetof` doesn't give you a pointer/reference to the class. It gives you an offset. And there is no standard ways in C++ to get the pointer to the object using the offset. As far as I know, there are only ways which rely on implementation defined details or technical UB. – eerorika Jul 25 '20 at 23:26
  • @eerorika I think I misunderstood what you meant by _"all implementations that I've seen rely on something that is either technically undefined behaviour."_ I thought you were talking about `offsetof()` but now I think you were talking about _using_ `offsetof()` to do what OP requests. In that case, what you said in your answer makes sense. I think we are in agreement, I just didn't catch exactly what that sentence referred to. – cdhowie Jul 25 '20 at 23:28
  • 1
    @cdhowie Ah, I see how it is ambiguous. I added more words to hopefully help with that. – eerorika Jul 25 '20 at 23:31
0

Alert: don't do it regularly since it's very easy to mess thing out

#include <iostream>

class example{
public:
    int var;
};


int main(){

    example exampleObject{15};

    int* point=&exampleObject.var;

    example* objPointer = reinterpret_cast<example*>(point);
    std::cout << objPointer->var;
    return 0;
}

now objPointer will points to the original object, however this works only because of how object are managed, and so because since var is the first property of a class without a base class, its address will be the same of the object, but if you have something like this:

class example{
public:
    int var;
    int var2;
};


int main(){

    example exampleObject{15, 16};

    int* point=&exampleObject.var2;

    example* objPointer = reinterpret_cast<example*>(point);
    std::cout << objPointer->var2;
    return 1;
}

It won't work, because var2 is not saved in the same address where the object is saved, since there is var saved before it

Alberto Sinigaglia
  • 12,097
  • 2
  • 20
  • 48
  • This works only because `var` is the 1st (and only) data member of `example`, so the address of `var` is the same as the address of `exampleObject`. If `example` had other data members, and `var` wasn't the 1st member, then this approach would not work as shown anymore. You would have to adjust the address using `offsetof(example, var)` – Remy Lebeau Jul 25 '20 at 23:06
  • @RemyLebeau yes, i've mentioned that on the second example – Alberto Sinigaglia Jul 25 '20 at 23:07
  • @eerorika mh, really? i thought that the use of `reinterpret_cast` will automatically cause UB – Alberto Sinigaglia Jul 25 '20 at 23:07
  • @Berto99 `reinterpret_cast` never causes UB itself. It's how the reinterpreted pointer is used that may be UB. But also, using reinterpreted pointer is not always UB. If that were the case, then there would not be any point for the language to allow such casts. This is one of the special cases where the reinterpretation is allowed. – eerorika Jul 25 '20 at 23:10
  • @Berto99 I hadn't seen that yet when I posted my comment. – Remy Lebeau Jul 25 '20 at 23:11