Yanshof
Yanshof

Reputation: 9926

Find sizeof type object ?

I define this struct

public enum E1 : byte
{
    A,
    B,
    C
}

public enum E2 : byte
{
    D,
    E,
    F
}

[StructLayout(LayoutKind.Sequential, Pack=4)]
public struct SomeStruct
{
    public Int32  _var1; //4 byte
    public E1     _var2; //1 byte(because Pack is 4 this will take 4 byte)
    public E2     _var3; //1 byte(because Pack is 4 this will take 4 byte) 
    public UInt64 _var4; //8 byte
}

Now, because i add the StructLayout attribute with Pack=4 i expect that each of the variable that i define will keep the alignment of data fields to be 4.

So i expect that the

 int val = Marshal.SizeOf( typeof( SomeStruct ) );

but i check the code and i found that val is 16

So how it can be ?
Where is my misunderstanding ?

Upvotes: 4

Views: 130

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477759

SizeOf specifies the size in bytes.

Now if we do the math we see that:

  • An Int32 has 32 bits thus 4 bytes.
  • An Int64 has 64 bits thus 8 bytes.
  • Both enums are encoded on a byte, thus 1 byte.

Together that's 1+1+4+8 thus 14 bytes.

But: most systems don't like to work with bytes: they fetch and store data using words (16 bit), or dwords (32 bit). Since minimizing the data structure would result in operations to cut off data and reinstert it, it is packed with tailing zeros.

If you thus work on such system, the compiler might like to pack data together in chunks of 16 bit (or higher). These are easier: if you for instance want to access an element in an array, it doesn't have to fetch two words into memory because the struct is splitted among the words.

An experiment to demonstrate how the data is packed:

using System.Runtime.InteropServices;

namespace Foo {

    public enum E1 : byte
    {
        A,
        B,
        C
    }

    public enum E2 : byte
    {
        D,
        E,
        F
    }


    [StructLayout(LayoutKind.Sequential,Pack=4)]
    public struct SomeStruct
    {
        public int  _var1; //4 byte
        public byte     _var2; //1 byte(because Pack is 4 this will take 4 byte)
        public byte     _var3; //1 byte(because Pack is 4 this will take 4 byte) 
        public ulong _var4; //8 byte

        public SomeStruct (int var1, byte var2, byte var3, ulong var4) {
             this._var1 = var1;
             this._var2 = var2;
             this._var3 = var3;
             this._var4 = var4;
        }

    }

}

Foo.SomeStruct i = new Foo.SomeStruct(1302,5,17,1425);
Marshal.SizeOf( typeof( Foo.SomeStruct ) );
sizeof(Foo.SomeStruct);
int size = sizeof(Foo.SomeStruct);
byte[] result = new byte[size];
IntPtr buffer = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(i, buffer, false);
Marshal.Copy(buffer, result, 0, size);

results in:

{ 22, 5, 0, 0, 5, 17, 0, 0, 145, 5, 0, 0, 0, 0, 0, 0 }

The struct is thus encoded as:

0                32   40   48       64                              128
+----------------+----+----+--------+--------------------------------+
| _var1          | _v2| _v3| ------ | _var4                          |
+----------------+----+----+--------+--------------------------------+

If you use int/uint to encode the variable, one gets:

{ 22, 5, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 5, 0, 0, 0, 0, 0, 0 }

or:

0       32        64       96      128              192
+--------+--------+--------+--------+----------------+
| _var1  |  _var2 | _var3  | ------ | _var4          |
+--------+--------+--------+--------+----------------+

Upvotes: 3

Related Questions