Inside C# 4 Data Structs
Written by Mike James   
Tuesday, 07 September 2010
Article Index
Inside C# 4 Data Structs
Being exact
The API
Serialising structs
Manual mashaling
Struct-to-pointer

Banner

Being Explicit

If you really do want to specify the space allocated to any particular field you can use Explicit. For example:

[StructLayout(LayoutKind.Explicit)]
public struct struct1
{
[FieldOffset(0)]
public byte a; // 1 byte
[FieldOffset(1)]
public int b;  // 4 bytes
[FieldOffset(5)]
public short c;// 2 bytes
[FieldOffset(7)]
public byte d; // 1 byte
}

produces an 8-byte struct without any padding bytes. In this sense it is equivalent to Pack=1 which is much simpler to use. However Explicit really does give you complete control should you need it. For example:

[StructLayout(LayoutKind.Explicit)]
public struct struct1
{
[FieldOffset(0)]
public byte a;  // 1 byte
[FieldOffset(1)]
public int b;   // 4 bytes
[FieldOffset(10)]
public short c; // 2 bytes
[FieldOffset(14)]
public byte d;  // 1 byte
}

produces a 16-byte struct with extra bytes following the b field.

Until C# 2.0 the main use of an Explicit layout was to provide fixed length buffers for use in DLL calls, for example. You simply cannot declare a fixed size array within a struct because initialising fields isn’t permitted. That is:

public struct struct1
{
public byte a;
public int b;
byte[] buffer = new byte[10];
public short c;
public byte d;
}

generates an error. If you want a 10-byte buffer one way of doing it is:

[StructLayout(LayoutKind.Explicit)]
public struct struct1
{
[FieldOffset(0)]
public byte a;
[FieldOffset(1)]
public int b;
[FieldOffset(5)]
public short c;
[FieldOffset(8)]
public byte[] buffer;
[FieldOffset(18)]
public byte d;
}

This leaves a block of 10 bytes for the buffer at the end of the struct.

There are a number of interesting points in this declaration. The first is why use an offset of 8? The reason is that you can’t start an array on an odd address boundary.

If you use 7 you will see a runtime error informing you that the struct cannot be loaded because of an alignment problem. This is important because it means you can cause problems by using Explicit if you don’t know what you are doing.

The second is that the entire struct has additional bytes added to the end to bring its size up to a multiple of 8 bytes. The compiler still gets involved with memory allocation. In practice, of course, any external structure that you are trying to convert to a C# struct should be correctly aligned and the problem shouldn’t arise.

Finally it is worth noting that you can’t refer to the 10-byte buffer using the array name, as in buffer[1] etc, because C# thinks that the buffer is unassigned. As you can’t use the array and it causes an alignment problems, a much better way to declare the struct is:

[StructLayout(LayoutKind.Explicit)]
public struct struct1
{
[FieldOffset(0)]
public byte a;  // 1 byte
[FieldOffset(1)]
public int b;   // 4 bytes
[FieldOffset(5)]
public short c; // 2 bytes
[FieldOffset(7)]
public byte buffer;
[FieldOffset(18)]
public byte d;  // 1 byte
}

To access the 10-byte field you have to use pointer arithmetic on buffer – which is of course considered “unsafe”. 

To allocate a fixed number of bytes to the last field in a struct you can use the Size= field in the StructLayout as in:

[StructLayout(LayoutKind.Explicit,Size=64)]

As of C# 2.0, fixed arrays are now allowed within structs and this more or less makes the above construction unnecessary.

It is worth noting that fixed arrays essentially use the same mechanism, i.e. fixed size allocation and pointers, hence this too is unsafe. If you need the fixed size buffers within a call to a DLL then the probably the best method is to use explicit marshaling for the arrays, which is considered “safe”. Let’s take a look at all three methods in use.

Banner

<ASIN:0321658701>
<ASIN:1451531168>

<ASIN:0321637003>

<ASIN:0596800959>

<ASIN:047043452X>

<ASIN:0123745144>



Last Updated ( Tuesday, 28 September 2010 )