views:

420

answers:

3

In the .NET struct design guidelines, it gives the maximum sensible size of a struct as 16 bytes. How do you determine how large your struct is, and is it affected by the architecture your program is running on? Is this value 32-bit only, or for both archs?

+6  A: 

It's not a hard-and-fast value - it's just a guideline, a rule of thumb. Depending on the exact situation, 24 or even 32 bytes could still be perfectly justifiable - but if your struct gets that large you should really be asking yourself whether it's appropriate as a struct in the first place. It may be - in which case taking the hit of copying those 32 bytes around any time you perform an assignment or pass an argument into a method (etc) may be the right thing to do; in other cases you should really be using a class.

As for how you determine how big your struct is - usually it's fairly obvious, because usually a value type only contains other value types. If your struct contains references (or an IntPtr/UIntPtr), that's more of a problem - but that's pretty rare. (As Mehrdad points out, there's also the issue of padding for the sake of alignment.)

Then again, I find it extremely rare that I want to write my own struct anyway. What's your situation?

Jon Skeet
Hey Jon I edited your post, let me know if it's a mistake. Thanks.
Joan Venge
Joan: That's fine, thanks :)
Jon Skeet
+3  A: 

In .Net the majority of types do not change size between a 32 and 64 bit program. The only 2 value types defined by the framework which will change their size based on the platform are

  • IntPtr
  • UIntPtr

Unless you have one of these directly or indirectly in your struct, it should not change size between platforms.

As Mehrdad pointed out, the other two classes of fields which will change size based on platform are

  • Pointers
  • Reference types

All of these types though will change in the exact same way. 4 bytes on a 32 bit platform and 8 bytes on a 64 bit platform.

JaredPar
@Mehrdad, yeah I shouldn't have limited the discussion to value types.
JaredPar
+2  A: 

Yes, the size of a struct is affected by the architecture. C# structs in 32bit are aligned at 4 byte boundaries, and in 64bit they are 8 byte aligned.

Example:

struct Foo
{
   int bar;
}

Instances of this struct will take up 4 bytes in 32bit processes, and 8 bytes in 64bit processes, even though the "int bar" takes just 4 bytes on both 32bit and 64bit processes.

Update:

I did some testing with this. I wrote this code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    struct Bar
    {
        int a;
    }

    struct Foo
    {
        Uri uri;
        int a;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Foo[] foo;
            long fooBefore = System.GC.GetTotalMemory(true);
            foo = new Foo[10];
            long fooAfter = System.GC.GetTotalMemory(true);

            Bar[] bar;
            long barBefore = System.GC.GetTotalMemory(true);
            bar = new Bar[10];
            long barAfter = System.GC.GetTotalMemory(true);

            Foo aFoo = new Foo();
            Bar aBar = new Bar();

            System.Console.Out.WriteLine(String.Format("[Foo] Size of array of 10: {0}, Marshal size of one: {1}", fooAfter - fooBefore, System.Runtime.InteropServices.Marshal.SizeOf(aFoo)));
            System.Console.Out.WriteLine(String.Format("[Bar] Size of array of 10: {0}, Marshal size of one: {1}", barAfter - barBefore, System.Runtime.InteropServices.Marshal.SizeOf(aBar)));
            System.Console.ReadKey();
        }
    }
}

As a 64bit process, I get this output:

[Foo] Size of array of 10: 208, Marshal size of one: 16
[Bar] Size of array of 10: 88, Marshal size of one: 4

As a 32bit process, I get this output:

[Foo] Size of array of 10: 92, Marshal size of one: 8
[Bar] Size of array of 10: 64, Marshal size of one: 4

Observations:

  • The simple struct, Bar, seems to take 4 bytes on both 32bit and 64bit processes
  • The other struct, Foo, seems to take 8 bytes on 32bit (4 bytes for the int, and 4 bytes for the reference to the Uri), but 16 bytes on 64bit (4 bytes for the int, 8 bytes for the reference to the Uri, and I think 4 bytes for alignment)
Alex Black
JaredPar seems to disagree - do you know where the information can be obtained?
thecoop
Hmm, maybe I'm mistake here. I'll look into it a bit more.
Alex Black
I updated the answer with some interesting results.
Alex Black
++, interesting findings...
thecoop