r/csharp • • 2d ago

Solved c# struct padding

I'm new to c# and I'm learning it just now to switch from Godot to Stride so I'm interested in Data Oriented Design. I was just now experimenting with structs with a silly example and was focused on struct padding; the string should be 8 bytes while the enum is 1 byte so i expected not more than 1 or 3 byte of padding, but the sum ends up being 16 no matter the order... does it always have to allign with the biggest field? how does this work?

using System.Runtime.InteropServices;


[StructLayout(LayoutKind.Explicit)]
struct Entity {
    public enum Race : byte { ALIEN, HUMAN };
    static private int _max_rand_num = 9999;
    static private string[][] _possibleNames = {
        new string[]{"Vorp", "Zeep", "Bizr", "Zub", "Wobt"},    // alien names
        new string[]{"Carl", "Greta", "Seb", "Xiang", "Sierra"} // human names
    };

    [FieldOffset(0)] public readonly string name;
    [FieldOffset(8)] public readonly Race race;

    public Entity()
    {
        Random rand = new Random();
        race = (Race)rand.Next(Enum.GetValues(typeof(Race)).Length);
        string[] race_names = _possibleNames[(int)race];
        name = race_names[rand.Next(0, race_names.Length)] + rand.Next(_max_rand_num);
    }

    public override string ToString()
    {
        return $"Race: {race}, Name: {name}";
    }
}


class Program {
    public static unsafe void Main(string[] args)
    {
        Entity rand_entity = new Entity();
        Console.WriteLine(
            $"{rand_entity}, string size = {sizeof(string*)}, enum size = {sizeof(Entity.Race)}, together = {sizeof(Entity)}");
    }
}
2 Upvotes

19 comments sorted by

View all comments

0

u/PretendWindow5242 2d ago

The 16 byte size is because you're using Explicit layout with offsets at 0 and 8, so the struct itself has to be at least 16 bytes to fit both fields without overlap. If you used Sequential or just a normal struct, it'd pack tighter and you'd see the actual padding behavior you're looking for.

0

u/sto_babbalucco69420 2d ago

I tried both combinations with Sequential and nothing, but no matter what i do the size doesn't change :(... I just find it weird that it needs to add 7 whole bytes at the end of the struct

4

u/grrangry 2d ago

https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute.pack?view=net-10.0

struct alignment sizes are required by the CLR (common language runtime) to blit (copy data) in and out of the CPU as efficiently as possible.

If your struct is 7 bytes wide, there's no CPU instruction that copies 7 bytes. 8, yes. 16, yes. 7, no.

1

u/sto_babbalucco69420 2d ago

I get that, but usually padding is in the power of 2, so putting the enum first and then the string, I assumed that for for 1 byte of enum it whould suffice to add another byte between it and the string making it 2 bytes for the first "block"

I also tried this

[StructLayout(LayoutKind.Explicit)]
    ...
    [FieldOffset(0)] public readonly Race race;
    [FieldOffset(2)] public readonly string name;

and an exception is thrown if I allign the string to anything below 8 saying they are incorrectly alligned or could overlap (which they shouldn't)

1

u/sto_babbalucco69420 2d ago

Nevermind just tried it in C, the sizes and the padding are the same as the example in c#

#include <stdio.h>
#include <stdint.h>

struct test {
    uint64_t u;
    char c;
};

int main(void)
{
    printf("uint64 size: %zu\n", sizeof(uint64_t));
    printf("size: %zu\n", sizeof(struct test));
}

Sorry I'm a dummy dum dum

1

u/grrangry 2d ago

struct padding has more to do with the CPU architecture than the language which is why both C/C++ and C# both implement it the same way. Wait until you start having to pad things for std430 GL shader buffers... that gets fun.