5

Here is my code

#include <bits/stdc++.h>


class A{
    int val;
    char c;
};
class B:public A{
    char val;
};

struct C{
    int val;
    char c;
};
struct D:public C{
    char val;
};


int main()
{
    std::cout<<sizeof(B)<<std::endl; //8
    std::cout<<sizeof(D)<<std::endl; //12

}

Why class have different alignment with struct


*** Dumping AST Record Layout
   0 | class A
   0 |   int val
   4 |   char c
     | [sizeof=8, dsize=5, align=4
     |  nvsize=5, nvalign=4]


*** Dumping AST Record Layout
   0 | class B
   0 |   class A (base)
   0 |     int val
   4 |     char c
   5 |   char val
     | [sizeof=8, dsize=6, align=4
     |  nvsize=6, nvalign=4]


*** Dumping AST Record Layout
   0 | struct C
   0 |   int val
   4 |   char c
     | [sizeof=8, dsize=8, align=4
     |  nvsize=8, nvalign=4]


*** Dumping AST Record Layout
   0 | struct D
   0 |   struct C (base)
   0 |     int val
   4 |     char c
   8 |   char val
     | [sizeof=12, dsize=9, align=4
     |  nvsize=9, nvalign=4]

M.M
  • 138,810
  • 21
  • 208
  • 365
vyyd
  • 53
  • 2
  • Welcome to stackoverflow.com. Please take some time to read [the help pages](http://stackoverflow.com/help), especially the sections named ["What topics can I ask about here?"](http://stackoverflow.com/help/on-topic) and ["What types of questions should I avoid asking?"](http://stackoverflow.com/help/dont-ask). Also please take the [tour] and read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Apr 30 '20 at 03:05
  • 2
    On an unrelated note, please read [Why should I not #include ?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) – Some programmer dude Apr 30 '20 at 03:05
  • You should probably specify your compiler and platform, since C++ only specifies a few restrictions on the possible result, but the exact results are defined by the target ABI. – aschepler Apr 30 '20 at 03:06
  • 2
    it would be less confusing if you did not use the same name `val` for different data members of the same struct, please change that and update the question – M.M Apr 30 '20 at 03:10
  • 1
    Can you try setting your `class` members to `public` and then print out the size. – WBuck Apr 30 '20 at 03:16
  • Adding `public` to classes, prints `12` for struct and class. – abhiarora Apr 30 '20 at 04:07
  • Similar question: https://stackoverflow.com/questions/53837373/standard-layout-and-tail-padding – M.M May 03 '20 at 22:35

2 Answers2

4

In the struct case consider this program:

void f(C& cx)
{
    cx.c = 'x';
}

int main()
{
    D d{};
    d.D::val = 'y';
    f(d);
    std::cout << d.D::val << '\n';
}

This code has to output y.

On your system, the A and C structs have size of 8 since there is a member with size 4 and a char, and the struct must be correctly aligned for its largest member. These structs have 4 bytes of int, 1 byte of char, and 3 padding bytes.

The assignment cx.c = 5; is allowed to modify the padding (any struct assignment can modify the struct padding). Therefore that padding cannot be used to store base class elements.

However no analogous example is possible with A and B because the data members of A are private. There cannot be a function void f(A& ax) { ax.c = 'x'; } so this concern does not arise, and the compiler can get away with using the padding area of A to store derived class members.


NB: Neither class is standard layout due to having data members in both the base and derived classes.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • This is very interesting, and this is also a good news even not the main against the public data members if I can say. Note this is the case not only for private but also protected data member, in short non-public. – bruno May 03 '20 at 15:47
  • -1. Why does this also work when the char is explicitly public and the int private?https://godbolt.org/z/4PeWecc7b This strongly contradicts your explanation. – JMC Oct 14 '22 at 07:58
1

Adding to @M.M answer, looks like even if you have public constructor and setter member function for class A, the compiler still stores the class B data members in padding area of class A (I was trying to force the compiler to not to use tail padding of class A but couldn't succeed).

A note can be found in class.mem/19 saying:

[ Note: Non-static data members of a (non-union) class with the same access control and non-zero size ([intro.object]) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified. Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions ([class.virtual]) and virtual base classes ([class.mi]). — end note ]

Adding more from this answer:

The standard requires members with the same access control to be grouped together in memory. That grouping decides how the object gets padded so changing it can/will change the size of the object.

And more from this answer:

The dsize, nvsize, and nvalign of these types are defined to be their ordinary size and alignment. These properties only matter for non-empty class types that are used as base classes. We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.

Thus, in your first example, A is not a POD for layout purposes and its tail padding can be used for B::val, but in your second example, it is a POD, and its tail padding cannot be reused.

#include <iostream>


class A {
    int val;
    char c;
public:
    A(int a, char b): val(a), c(b)
    {

    }
public:
    void setC(int a)
    {
        c = a;
    }
    char getC(void) const
    {
        return c;
    }
};

class B: public A {
    char val;
public:
    B(void): A(1,'2'), val('2')
    {

    }
public:
    char getVal(void) const
    {
        return val;
    }
};

struct C {
    int val;
    char c;
};
struct D: public C {
    char val;
};


int main()
{
    B a;
    a.setC(2370);
    std::cout << a.getVal() << " & " << a.getC() << std::endl;
    std::cout << sizeof(B) << std::endl; // 8
    std::cout << sizeof(D) << std::endl; // 12
    return 0;
}

Outputs:

2 & B
8
12 

To learn about memory order and alignment for classes, see this.

abhiarora
  • 9,743
  • 5
  • 32
  • 57