Reputation: 75854
While answering this question I was asked to provide standard quotes. I was shocked to find in the C++14 draft:
§ 3.9 Types [basic.types]
- The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T)
Hmm.. it doesn't say that the "unsigned char objects" must be contiguous in memory. Maybe it is implied by "sequence". Then I found a specific mention of "contiguous bytes of storage", but...
§ 1.8 The C++ object model [intro.object]
- [...] An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.
What? Only trivially copyable and standard-layout types are required to occupy contiguous bytes of storage? The rest of the types can have "holes" in the storage they occupy? I searched the rest of the standard but could not find any other relevance to "contiguous storage". Granted I am not that familiar with the standard.
If that is true (for me it would be the greatest shock about the standard) how does that go with sizeof
and pointer arithmetics? Are (were) there really any architectures/compilers that use (used) non-contiguous bytes of storage for types?
I really hope I am misinterpreting and/or missing something.
edit: I thought that maybe it is related to padding, but this interpretation would make sense only if trivially copyable or standard-layout types could not have padding. Then you say these types occupy contiguous bytes storage, and for the rest of types who can have padding you don't say that. But that is clearly not the case as any struct type can have padding.
Upvotes: 5
Views: 432
Reputation: 388
Yes. It is not only that objects (rather than types) can have "holes" in the storage they occupy, they may occupy char objects ch1, ch2, who belong to different sequences of contiguous bytes and whose addresses may or may not (i.e., neither &ch1 <= &ch2 nor &ch1 >= &ch2) be ordered. See §5.9 (3) and §1.7 (1) of the C++14-standard.
The observable "holes" are no instance of padding. Below, the object representation of object 'separate' including padding is mapped onto a non-contiguous subset of the available char objects:
#include <iostream>
struct A { int a; };
struct B : virtual A { int b; };
struct C : virtual A { int c; };
struct D : B,C { int d; };
int main()
{
D complete;
B contiguous;
B & separate = complete;
B * p[2] = {&separate, &contiguous};
// two possible layouts for B:
std::cout<< (int)((char*)(void*) &p[0]->a -(char*)(void*)&p[0]->b)<<" "<< sizeof(*p[0])<< "\n";
std::cout<< (int)((char*)(void*) &p[1]->a -(char*)(void*)&p[1]->b)<<" "<< sizeof(*p[1])<< "\n";
}
// sample output (Debian 8, amd64):
// 24 16
// 4 16
Note that the reported object size is smaller than you would expect assuming the layout was contiguous.
Still, there's absolutely nothing wrong with pointer arithmetics.
Upvotes: 0
Reputation: 32727
A class with a virtual base class may not be in contiguous bytes of memory (as some of the bytes between the virtual base and the rest of the class can be occupied by another class that also derives from the virtual base.
class A {
int a;
};
class B: virtual A {
int b;
};
class C: virtual A {
int c;
};
class D: public B, C {
int D;
}
The memory for an object of class D can be organized something like this:
-------
| a |
-------
| b |
-------
| c |
-------
| d |
-------
in increasing memory order. Object "C", which consists of ints "a" and "c", does not occupy consecutive memory locations.
Upvotes: 7