What is the size of a boolean In C#? Does it really take 4 bytes?

I have two structs with arrays of bytes and booleans:

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct1
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] values;
}

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct2
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public bool[] values;
}

And the following code:

class main
{
    public static void Main()
    {
        Console.WriteLine("sizeof array of bytes: "+Marshal.SizeOf(typeof(struct1)));
        Console.WriteLine("sizeof array of bools: " + Marshal.SizeOf(typeof(struct2)));
        Console.ReadKey();
    }
}

That gives me the following output:

sizeof array of bytes: 3
sizeof array of bools: 12

It seems to be that a boolean takes 4 bytes of storage. Ideally a boolean would only take one bit (false or true, 0 or 1, etc..).

What is happening here? Is the boolean type really so inefficient?

Jon Skeet
people
quotationmark

Firstly, this is only the size for interop. It doesn't represent the size in managed code of the array. That's 1 byte per bool - at least on my machine. You can test it for yourself with this code:

using System;
class Program 
{ 
    static void Main(string[] args) 
    { 
        int size = 10000000;
        object array = null;
        long before = GC.GetTotalMemory(true); 
        array = new bool[size];
        long after = GC.GetTotalMemory(true); 

        double diff = after - before; 

        Console.WriteLine("Per value: " + diff / size);

        // Stop the GC from messing up our measurements 
        GC.KeepAlive(array); 
    } 
}

Now, for marshalling arrays by value, as you are, the documentation says:

When the MarshalAsAttribute.Value property is set to ByValArray, the SizeConst field must be set to indicate the number of elements in the array. The ArraySubType field can optionally contain the UnmanagedType of the array elements when it is necessary to differentiate among string types. You can use this UnmanagedType only on an array that whose elements appear as fields in a structure.

So we look at ArraySubType, and that has documentation of:

You can set this parameter to a value from the UnmanagedType enumeration to specify the type of the array's elements. If a type is not specified, the default unmanaged type corresponding to the managed array's element type is used.

Now looking at UnmanagedType, there's:

Bool
A 4-byte Boolean value (true != 0, false = 0). This is the Win32 BOOL type.

So that's the default for bool, and it's 4 bytes because that corresponds to the Win32 BOOL type - so if you're interoperating with code expecting a BOOL array, it does exactly what you want.

Now you can specify the ArraySubType as I1 instead, which is documented as:

A 1-byte signed integer. You can use this member to transform a Boolean value into a 1-byte, C-style bool (true = 1, false = 0).

So if the code you're interoperating with expects 1 byte per value, just use:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)]
public bool[] values;

Your code will then show that as taking up 1 byte per value, as expected.

people

See more on this question at Stackoverflow