In C#, I am creating multiple different structs that contain 16 variables of type bool. I will have several different of these structs that will then be combined with other data types into more complex structs. I am needing to have them treated as being 2 bytes in length. In the code below, a variable created of type CtrlWord1 will give a length of 64 when I do a Marshal.SizeOf regardless of whether it is created with a Pack value of 0, 1 or 2.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
public bool a1;
public bool a2;
public bool a3;
public bool a4;
public bool a5;
public bool a6;
public bool a7;
public bool a8;
public bool b1;
public bool b2;
public bool b3;
public bool b4;
public bool c1;
public bool c2;
public bool c3;
public bool c4;
}
Although the bool
type in C# is only 1 byte in size ( sizeof(bool) == 1
), the CLR defaults to marshalling it as the unmanaged BOOL
type. This is the size you get when you call Marshal.SizeOf
.
BOOL
is a typedef in the Windows SDK headers for an int
, which is 4 bytes in size. Why? Because these headers were written for the C language at a time that this language did not have a first-class Boolean type. It does now, but the decisions are fixed in stone for backwards-compatibility reasons. The CLR marshals bool
types this way in order to be compatible with Windows API functions that use BOOL
values, since interop with the Windows API is the most common usage of P/Invoke. (The same reason that the default calling convention for P/Invoke signatures is stdcall instead of cdecl.)
To tell the CLR to treat your bool
s as 1-byte bools, rather than 4-byte BOOL
s, use the MarshalAs
attribute . Unfortunately, you have to use it 16 times:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
[MarshalAs(UnmanagedType.I1)] // marshal as a 1-byte signed int, not a 4-byte BOOL
public bool a1;
// etc.
}
This will ensure that your struct is only 16 bytes.
However, there's no magic attribute to generate a bitfield. You'll have to create and manage this yourself using the Int32
type. Or use the BitArray
type.
Glorin Oakenfoot said it much better than I could , so I will just quote him
Packing/layout is done at the byte level. That means a bool will never take less than a byte relying purely on packing. You'll have to do something a little more involved, such as use two private byte fields and multiple properties which refer to the appropriate bits within those bytes.
Here is a implementation of that, each item you increment the right hand side of the 1 << _
to move to the next bit field.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
private Int16 _backingField;
private void SetBitfield(Int16 mask, bool value)
{
if (value)
{
_backingField = (Int16)(_backingField | mask);
}
else
{
_backingField = (Int16)(_backingField & ~mask);
}
}
private bool GetBitfield(Int16 mask)
{
return (_backingField & A1_MASK) != 0;
}
private const Int16 A1_MASK = 1 << 0;
public bool a1
{
get { return GetBitfield(A1_MASK); }
set { SetBitfield(A1_MASK, value); }
}
private const Int16 A2_MASK = 1 << 1;
public bool a2
{
get { return GetBitfield(A2_MASK); }
set { SetBitfield(A2_MASK, value); }
}
private const Int16 A3_MASK = 1 << 2;
public bool a3
{
get { return GetBitfield(A3_MASK); }
set { SetBitfield(A3_MASK, value); }
}
private const Int16 A4_MASK = 1 << 3;
public bool a4
{
get { return GetBitfield(A4_MASK); }
set { SetBitfield(A4_MASK, value); }
}
//And so on
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.