Order of storage inside a structure / object

2019-02-19 00:54发布

问题:

Consider these two cases :

struct customType
{
   dataType1 var1; 
   dataType2 var2;
   dataType3 var3;
} ;

customType instance1;
// Assume var1, var2 and var3 were initialized to some valid values.

customType * instance2 = &instance1;    
dataType1 firstMemberInsideStruct = (dataType1)(*instance2);

class CustomType
{
   public:
       dataType1 member1;
       dataType2 member2;

       retrunType1 memberFunction1();

   private:
       dataType3 member3;
       dataType4 member4;

       retrunType2 memberFunction2();
};

customType object;
// Assume member1, member2, member3 and member4 were initialized to some valid values.

customType *pointerToAnObject = &object ;
dataType1 firstMemberInTheObject = (dataType1) (*pointerToAnObject);

Is it always safe to do this ?

I want to know if standard specifies any order of storage among -

  1. The elements inside a C structure.
  2. Data members inside an object of a C++ class.

回答1:

C99 and C++ differ a bit on this.

The C99 standard guarantees that the fields of a struct will be laid out in memory in the order they are declared, and that the fields of two identical structs will have the same offsets. See this question for the relevant sections of the C99 standard. To summarize: the offset of the first field is specified to be zero, but the offsets after that are not specified by the standard. This is to allow C compilers to adjust the offsets of each field so the field will satisfy any memory alignment requirements of the architecture. Because this is implementation-dependent, C provides a standard way to determine the offset of each field using the offsetof macro.

C++ offers this guarantee only for Plain old data (POD). C++ classes that are not plain old data cannot be treated like this. The standard gives the C++ compiler quite a bit of freedom in how it organizes a class when the class uses multiple inheritance, has non-public fields or members, or contains virtual members.

What this means for your examples:

dataType1 firstMemberInsideStruct = (dataType1)(*instance2);

This line is okay only if dataType1, dataType2, and dataType3 are plain old data. If any of them are not, then the customType struct may not have a trivial constructor (or destructor) and this assumption may not hold.

dataType1 firstMemberInTheObject = (dataType1) (*pointerToAnObject);

This line is not safe regardless of whether dataType1, dataType2, and dataType3 are POD, because the CustomType class has private instance variables. This makes it not a POD class, and so you cannot assume that its first instance variable will be ordered in a particular way.



回答2:

9.0.7

A standard-layout class is a class that: — has no non-static data members of type non-standard-layout class (or array of such types) or reference, — has no virtual functions (10.3) and no virtual base classes (10.1), — has the same access control (Clause 11) for all non-static data members, — has no non-standard-layout base classes, — either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and — has no base classes of the same type as the first non-static data member.108

9.2.14

Nonstatic data members of a (non-union) class with the same access control (Clause 11) 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 (11). 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 (10.3) and virtual base classes (10.1).

9.2.20

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ 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 ]



回答3:

It's not always safe to do so. If the classes have virtual methods, it most definitely is not. Data members are guaranteed to appear in the same order for the same access level chunk, but these groups can be reordered.

In order to be safe with these type of casts, you should provide a conversion constructor or a cast operator, and not rely on implementation details.



回答4:

Typically in a C struct members are stored in the order that they are declared. However the elements must be aligned properly. Wikipedia has a good example of how this works.

I will re-iterate here:

If you have the following struct

struct MixedData
{
    char Data1;
    short Data2;
    int Data3;
    char Data4;
};

padding will be inserted in between differing data types in order to assure the proper byte-alignment. chars are 1-byte aligned, shorts are 2-byte aligned, ints are 4-byte aligned, etc.

Thus to make Data2 2-byte aligned, there will be a 1-byte padding inserted between Data1 and Data2.

It is also worth mentioning that there are mechanisms that can change the packing alignment. See #pragma pack.