1

In C, the first element of a struct has the same address as the struct itself. Is the same true for non-POD structs in C++ if the first element is POD?

For example, given this code:

struct bar
{
    struct bar *p1, *p2;
    unsigned char h;
}

struct foo
{
    struct bar node;
    int a;

    private:
        int x;
};

int main(void)
{
struct foo A;
struct bar *ptr;

ptr = &A.node;

struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node)));

return 0;
}

I get an "invalid access to non-static data member 'foo::node' of NULL object ... perhaps the offsetof macro was used incorrectly" warning.

My question is this - can I assume that 'node' is at the beginning of foo (same address), even though this is a non-POD struct?

If yes, I can just use a reinterpret cast, and I get no warning:

struct foo *o = reinterpret_cast<struct foo*>(ptr);

So, if the C++ struct starts with public POD data, will the first element share the address of the object per the standard?

Thanks

--edit-- In a class with no virtual methods or superclass, is it safe to assume (address of first member variable) == this? was pointed to as a possible answer. It mentions "Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allo- cated so that later members have higher addresses within a class object. " That doesn't really address whether or not the first element in my case is the same address as the object itself.

I think since the standard does not say anything about it, I cannot assume it is the case. I may need to change the objects at the end of the struct to pointers, and deal with allocating them as needed.

Community
  • 1
  • 1
Marc
  • 32
  • 7
  • In C++, `struct` is not required for declaring a variable of type struct. – Adrian Maire Mar 17 '17 at 14:39
  • 2
    From [cppreference](http://en.cppreference.com/w/cpp/language/data_members): *"Members with different access control are allocated in unspecified order (the compiler may group them together)"* - so that should mean you can't guarantee that `bar` is allocated as the first member – UnholySheep Mar 17 '17 at 14:42
  • 1
    `struct` may have virtual table pointer and as match as I know on some platform that is on the begining. So no. – Slava Mar 17 '17 at 14:43
  • @UnholySheep - Assume no private members, but some non-POD element, like an Object with it's own public/private, was part of my struct. This would make my struct non-POD but all would have the same access control. Could I guarantee node is at the start? – Marc Mar 17 '17 at 14:51
  • Possible duplicate of [In a class with no virtual methods or superclass, is it safe to assume (address of first member variable) == this?](http://stackoverflow.com/questions/2624628/in-a-class-with-no-virtual-methods-or-superclass-is-it-safe-to-assume-address) – Raymond Chen Mar 17 '17 at 16:26

2 Answers2

1

The standard says in [class.mem]/19

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any). [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. —end note ]

So if the class is a standard-layout class which yours is not the standard guarantees the address of first member is the address of the class. In your case it states that it is the address of it's first base class. Since you do not have a base object that means the first member shares the address of the object as there can never be padding at the start of a object. This means the address of a foo is the address of it's bar member and since bar is a standard layout class it would also be the address of p1.

Do note though that trying to get any member after the first member is undefined behavior. Class types (this includes struct) are allowed to have padding between any of the members of the class for alignment purposes. This means you never know where exactly the other members are in relation to the first member.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • So if "bar" was something like: struct foo { struct bar node; int a; objectx X; objecty Y; }; I could get the address of "foo" from a reinterpret_cast of "node", but nothing after that? – Marc Mar 17 '17 at 14:56
  • Something I'm not sure about: `reinterpret_cast`ing the pointer/address of the object to it's first non-static member should break the strict aliasing rule, shouldn't it? Or does this fall into the category of *"AliasedType and DynamicType are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T"* (from the [`reinterpret_cast`](http://en.cppreference.com/w/cpp/language/reinterpret_cast) reference page) – UnholySheep Mar 17 '17 at 15:00
  • @Marc Yes. If `foo` was `struct foo { struct bar node; int a; objectx X; objecty Y; };` then it would be a standard layout type and `&foo_object` and `&foo.object.node` would be the same address. You do know know what the address of `a` is since there could be padding between `node` and `a`. – NathanOliver Mar 17 '17 at 15:01
  • As a note, be careful with polymorphism: a cast from child class to parent class actually change the access address. `&(Parent)obj !=&obj` It is related but not always trivial to detect. – Adrian Maire Mar 17 '17 at 15:05
  • @NathanOliver Wouldn't struct foo { struct bar node; int a; objectx X; objecty Y; }; only be of standard-layout type if "objectx" and "objecty" were basic objects with the same access control throughout? My concern is that "objectx" has public and private sections. – Marc Mar 17 '17 at 15:26
  • @Marc Yeah. I missed you called the, `objectx`. All members need to be standard layout as well to be a standard layout class. – NathanOliver Mar 17 '17 at 15:28
  • @NathanOliver All good. The standard doesn't really seem to say what happens at the beginning in this case, so I do not think it is safe to assume. I will probably have to make the objects at the end of the struct pointers, and allocate as needed. That will make the struct POD again. – Marc Mar 17 '17 at 17:42
-4

As per Slava since virtual tables can be part of struct so the short answer is no. Now vtables are not part of standard. They are just a mechanism to implement virtual thing and thus achieving polymorphism. The answer is still no for all practical reasons.

Please see following code:

#include <iostream>

using namespace std;

struct bar
{
    struct bar *p1, *p2;
    unsigned char h;
};

struct foo
{
    struct bar node;
    int a;

    private:
        int x;
};

int main(void)
{
    struct foo A;
    struct bar *ptr;

    A.node.p1 = new bar();
    A.node.p2 = new bar();
    ptr = &A.node;

    struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node)));
    cout << n->a << endl;

    return 0;
}

Code is compiled with g++ -Wall -Werror -pedantic -std=c++14 test.cpp. The problem is what I think is that you did not allocate memory for p1 and p2.

Shiv
  • 1,912
  • 1
  • 15
  • 21
  • And what is this supposed to show? And how does it answer the question (which specifically asks about the standard)? – UnholySheep Mar 17 '17 at 14:44
  • @UnholySheep Well, pedantic flag ensures that this code is standards compliant and has no problems. Just that OP has not written it correctly – Shiv Mar 17 '17 at 14:45
  • Thank you for the comment, but the allocation of p1 and p2 is irrelevant (they exist as pointers). – Marc Mar 17 '17 at 14:48
  • @Marc I wrote this to explain "I get an "invalid access to non-static data member 'foo::node' of NULL object ... perhaps the offsetof macro was used incorrectly" warning." in your question. – Shiv Mar 17 '17 at 14:51
  • @user902384 - This does not address the question about what the standard actually says, ie. for a non standard layout, will the start address be the same as the first element. – Marc Mar 17 '17 at 15:49