Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Memory layout and inheritance

4 views
Skip to first unread message

Mathias Gaunard

unread,
Feb 28, 2007, 5:38:57 PM2/28/07
to
Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
(only tested with GCC on x86)
Is there a restriction in the language preventing this from being the
case ?

#include <iostream>

struct Foo
{
int a;
bool i;
bool b;
};

struct A
{
int a;
bool i;
};

struct B : public A
{
bool b;
};

int main()
{
std::cout << sizeof(Foo) << sizeof(B) << std::endl;
}


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

red floyd

unread,
Mar 1, 2007, 2:55:06 AM3/1/07
to
Mathias Gaunard wrote:
> Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
> (only tested with GCC on x86)
> Is there a restriction in the language preventing this from being the
> case ?
>
> #include <iostream>
>
> struct Foo
> {
> int a;
> bool i;
> bool b;
> };
>
> struct A
> {
> int a;
> bool i;
> };
>
> struct B : public A
> {
> bool b;
> };
>
> int main()
> {
> std::cout << sizeof(Foo) << sizeof(B) << std::endl;
> }
>
>

Because a compiler is free to pad structures as it sees fit.

This is implementation dependent.

My guess is as follows.

Foo has no padding between i and b.

B has padding between i and b, because A is padded out to
$SOME_ALIGNMENT_VALUE.

Again, this is a guess, the Standard imposes no requirements one way or
the other.

Arun

unread,
Mar 1, 2007, 5:06:49 AM3/1/07
to
{ Quoted banner removed, please don't quote it. -mod }

On Feb 28, 5:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:
> Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
> (only tested with GCC on x86)
> Is there a restriction in the language preventing this from being the
> case ?
>
> #include <iostream>
>
> struct Foo
> {
> int a;
> bool i;
> bool b;
>
> };
>
> struct A
> {
> int a;
> bool i;
>
> };
>
> struct B : public A
> {
> bool b;
>
> };
>
> int main()
> {
> std::cout << sizeof(Foo) << sizeof(B) << std::endl;
>
> }
>

This depends on the compiler and the OS on which you run it.
Lets take an example of an OS where 4 bytes is a word.
Struct Foo will fit in two words or 8 bytes.

Where as B which has two components strictly:
1. A part of B: Requires two words with some padding ( 1 word for the
int and 1 word for bool (with padding))
2. B part of B : Requires one word with some padding ( 1 word for bool
(with padding))
So B requires 3 words or 12 bytes in total.

Jack Klein

unread,
Mar 1, 2007, 5:07:01 AM3/1/07
to
On Wed, 28 Feb 2007 16:38:57 CST, "Mathias Gaunard"
<louf...@gmail.com> wrote in comp.lang.c++.moderated:

> Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
> (only tested with GCC on x86)

Why should it not be? They are not the same types. You cannot assign
a Foo to a B, or a B to a Foo. Why do you expect that they would have
the same size?

> Is there a restriction in the language preventing this from being the
> case ?

No, there is no restriction. But there is neither a requirement nor a
guarantee that they will be the same. Or that they will have
different sizes.

> #include <iostream>
>
> struct Foo
> {
> int a;
> bool i;
> bool b;
> };
>
> struct A
> {
> int a;
> bool i;
> };
>
> struct B : public A
> {
> bool b;
> };
>
> int main()
> {
> std::cout << sizeof(Foo) << sizeof(B) << std::endl;
> }

Consider this paragraph from section 3.9 of the C++ standard:

"Object types have alignment requirements (3.9.1, 3.9.2). The
alignment of a complete object type is an implementation defined
integer value representing a number of bytes; an object is allocated
at an address that meets the alignment requirements of its object
type."

Also consider writing a program that outputs sizeof(B) and sizeof
(bool) and see what you get.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html

W. J. La Cholter

unread,
Mar 1, 2007, 5:07:18 AM3/1/07
to
"Mathias Gaunard" <louf...@gmail.com> wrote in
news:1172694154....@h3g2000cwc.googlegroups.com:

> Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
> (only tested with GCC on x86)
> Is there a restriction in the language preventing this from being the
> case ?
>
> #include <iostream>
>
> struct Foo
> {
> int a;
> bool i;
> bool b;
> };
>
> struct A
> {
> int a;
> bool i;
> };
>
> struct B : public A
> {
> bool b;
> };

[snip]

The packing is an implementation-defined thing. sizoef(A) also
happens to be sizeof(Foo). So B is necessarily going to include the
additional bool and align up to the next 4 byte quantity. If you need
specific alignment, use the #pragma pack to ensure A is smaller:

#pragma pack(push,2)


struct A
{
int a;
bool i;
};

struct B : public A
{
bool b;
};

#pragma pack(pop)

That #pragma is pretty common.

Usually one cares about these things for binary compatibility such as
with protocols or encoding. If you're getting into situations where
you'd like to be 64-bit clean, also, be sure to use types that don't vary
based on platform, e.g., int32_t, found in the common (but not in
Windows) non-C++ C99 <stdint.h>. Otherwise, you'll be in for some
surprises when you migrate.

James Kanze

unread,
Mar 1, 2007, 5:10:35 AM3/1/07
to
On Feb 28, 11:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:
> Why, in the following code, is sizeof(Foo) different from sizeof(B) ?

The real question is why you expect them to be the same.

> (only tested with GCC on x86) Is there a restriction in the
> language preventing this from being the case ?

The language makes only very limited guarantees with regards to
the layout of PODs, and even less for non-PODs. There are
almost no restrictions; it's up to the compiler.

> #include <iostream>

> struct Foo
> {
> int a;
> bool i;
> bool b;
>
> };

Just a guess: on a 32 bit machine (x86), sizeof(Foo) is 8.
There's no guarantee of this, of course, and on an x86, it could
also be 6 and the code would work. (That's not the case on
other processors, of course; if Foo had a size of 6 on a Sparc,
accessing Foo::a on the second element in an array would cause a
core dump.)

> struct A
> {
> int a;
> bool i;
> };

sizeof(A) is probably also 8. Alignment oblige.

> struct B : public A
> {
> bool b;
> };

Off hand, I'd expect 12: an A (of 8 bytes) followed by a bool,
and enough padding to ensure alignment. But other arrangements
are possible. (The empty base class optimization doesn't apply,
since A is not empty. And I'm not sure that the compiler is
allowed to reduce the size of the A sub-object otherwise.)

About the only case I'd expect B and Foo to have the same size
is if the compiler ignored alignment restrictions. While this
is allowed by the x86 architecture, it has significant run-time
cost, and of course, many other architectures don't allow it at
all---ignore alignment on a Sparc, and you get a core dump.
(There is one other case where B and Foo might have the same
size: on a machine where bool was the same size as int. Either
an embedded processor, where sizeof(int)==1, or a word addressed
machine, where the implementor decided to put bool on a word,
rather that a byte, on the grounds that byte access was
significantly slower.)

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Larry Evans

unread,
Mar 1, 2007, 1:11:05 PM3/1/07
to
On 03/01/2007 04:10 AM, James Kanze wrote:
> On Feb 28, 11:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:
>
>>Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
>
[snip]

>>#include <iostream>
>
>
>>struct Foo
>>{
>> int a;
>> bool i;
>> bool b;
>>
>>};
>
[snip]

>
>>struct A
>>{
>> int a;
>> bool i;
>>};
>
>
> sizeof(A) is probably also 8. Alignment oblige.
>
>
>>struct B : public A
>>{
>> bool b;
>>};
>
>
> Off hand, I'd expect 12: an A (of 8 bytes) followed by a bool,
> and enough padding to ensure alignment. But other arrangements
> are possible. (The empty base class optimization doesn't apply,
> since A is not empty. And I'm not sure that the compiler is
> allowed to reduce the size of the A sub-object otherwise.)
>
> About the only case I'd expect B and Foo to have the same size
> is if the compiler ignored alignment restrictions. While this
> is allowed by the x86 architecture, it has significant run-time
> cost, and of course, many other architectures don't allow it at
[snip]
In http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2165.pdf
there's mention of:

template <std::size_t Len, class ... Types> struct aligned_union

whose nested typedefs describe the alignment of a uniont of the
given types, Types... (using the variadic template notation for
unpacking the parameter pack ...Types, as described here:
http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.pdf
) How would a similar template be defined for calculating
the offsets of a struct containing the same types? The motivation
doing this is so that a space efficient tuple can be defined which
is as space efficient as boost fusion's tuple:

http://boost.cvs.sourceforge.net/boost/boost/boost/fusion/tuple.hpp

The method used in fusion to achieve this efficiency is to generate
the member declarations with tools described in:

http://www.boost.org/libs/preprocessor/doc/index.html

IOW, the preprocessor tools generate:

T0 m0;
T1 m1;
...
TN mN;

where N=sizeof(Types...) (where sizeof... is defined in the
aforementioned .pdf file and returns the number of types
in Types...). IOW, aligned_struct<Len,T0,T1,...,TN>::offsets
would be an instance of:

template<std::size_t... Offsets>struct offsets_struct;

where sizeof(Offsets...) == sizeof(Types...),

Mathias Gaunard

unread,
Mar 1, 2007, 10:37:28 PM3/1/07
to
On Mar 1, 11:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
> On Feb 28, 11:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:
>
> > Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
>
> The real question is why you expect them to be the same.

Because I would expect my compiler to make the more efficient layout
for Foo, considering both space and alignment requirements.
And the more efficient layout for B would be Foo.

It seems

struct B : public A
{
/* some things */
};

is actually implemented as

struct B
{
A some_name;
/* some things */
};

which is less efficient than "inlining" the members of A, simply
because the non-used space of A induced by its alignment can't be
reused.

struct B
{
/* the members of A */
/* some things */
};

has the same capabilities and is more efficient.
You can still cast a B* to an A* safely.

It is kinda weird, since I've usually seen inheritance-like tricks in
C being implemented like this.


> About the only case I'd expect B and Foo to have the same size
> is if the compiler ignored alignment restrictions.

There is no need to ignore any alignement restriction for that to
happen.

James Kanze

unread,
Mar 2, 2007, 6:50:00 AM3/2/07
to
Mathias Gaunard wrote:
> On Mar 1, 11:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
> > On Feb 28, 11:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:

> > > Why, in the following code, is sizeof(Foo) different from sizeof(B) ?

> > The real question is why you expect them to be the same.

> Because I would expect my compiler to make the more efficient
> layout for Foo, considering both space and alignment
> requirements.

It makes the most efficient layout it can, considering both
space and alignment.

> And the more efficient layout for B would be Foo.

> It seems

> struct B : public A
> {
> /* some things */
> };

> is actually implemented as

> struct B
> {
> A some_name;
> /* some things */
> };

Of course it is. Nothing else works.

> which is less efficient than "inlining" the members of A, simply
> because the non-used space of A induced by its alignment can't be
> reused.

If you don't require the code to work, then you can optimize a
lot of things.

The standard already has a few exceptions where code is not
required to work: the empty base class optimization, and the
eliding of copy constructors. You're arguing for it to add
more.

Your A was a POD. Consider what happens when you memcpy into
it. Even if it wasn't, consider what happens when you copy it,
while initializing the elements of B differently.

A compiler can optimize for speed or for space. Optimized for
space, on an Intel, the size of Foo and the size of B would be
the same. And the size of A would be 5. Optimize for speed,
and you need to respect alignment. Most compilers today
optimize for speed, at least by default.

> struct B
> {
> /* the members of A */
> /* some things */
> };

> has the same capabilities and is more efficient.
> You can still cast a B* to an A* safely.

But you can't use the A* safely. For example:

static A someA ;

B someB ;
*static_cast< A* >( &someB ) = someA ;

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

--

Martin Bonner

unread,
Mar 2, 2007, 6:50:27 AM3/2/07
to
On Mar 2, 3:37 am, "Mathias Gaunard" <loufo...@gmail.com> wrote:
> On Mar 1, 11:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
>
> > On Feb 28, 11:38 pm, "Mathias Gaunard" <loufo...@gmail.com> wrote:
>
> > > Why, in the following code, is sizeof(Foo) different from sizeof(B) ?
>
> > The real question is why you expect them to be the same.
>
> Because I would expect my compiler to make the more efficient layout
> for Foo, considering both space and alignment requirements.
> And the more efficient layout for B would be Foo.
>
> It seems
>
> struct B : public A
> {
> /* some things */
>
> };
>
> is actually implemented as
>
> struct B
> {
> A some_name;
> /* some things */
>
> };
>
> which is less efficient than "inlining" the members of A, simply
> because the non-used space of A induced by its alignment can't be
> reused.
>
> struct B
> {
> /* the members of A */
> /* some things */
>
> };
>
> has the same capabilities and is more efficient.
> You can still cast a B* to an A* safely.

Not if you memcpy onto the A* (and you can do that because A is
POD).

In fact, even if you make A non-POD by adding a constructor, the
compiler internally might choose to implement the assignment operator
for A with an instruction that does "copy 64 bits", which would still
clobber the members of B if they were packed into the "unused" space
of A.

I think that if A is non-POD then B /may/ be layed out as Foo. Of
course, it doesn't have to be, and doing so might break the code of
people who assume that their base code is "almost POD". (*You* may
not care about such code, but compiler writers have to balance the
competing demands of making portable code more efficient and not
breaking non-portable code.)


>
> It is kinda weird, since I've usually seen inheritance-like tricks in
> C being implemented like this.
>
> > About the only case I'd expect B and Foo to have the same size
> > is if the compiler ignored alignment restrictions.
>
> There is no need to ignore any alignement restriction for that to
> happen.

Agreed.

0 new messages