當“C#默認封送 ≠ 你想要的 C++ 內存佈局”時,需要在結構體設置[MarshalAs]特性

【不需要】MarshalAs 的類型(⭐最安全)

這些是 blittable types,CLR 可以直接 memcpy

基礎數值類型

C#

C++

int

int32_t

long

int64_t

short

int16_t

byte

uint8_t

float

float

double

double

public int x;
public long ticks;
public double angle;

結構體(滿足條件)

✔ 字段都是 blittable
✔ 順序一致
✔ Pack 一致

[StructLayout(LayoutKind.Sequential)]
struct A
{
    public int x;
    public double y;
}

為什麼結構體要設置[StructLayout(LayoutKind.Sequential)]

如果不寫 StructLayout

//結構體
public struct Test
{
    public byte a;
    public int b;
}

CLR 可能按以下方式排

a | padding | padding | padding | b

或者

b | a | padding...

所以 C# 必須顯式告訴 CLR:不要亂排

LayoutKind 三個枚舉的含義

LayoutKind.Sequential

字段按照“聲明順序”依次排列,CLR 只會插入必要的 padding

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A
{
    public byte a;
    public int b;
}

Pack=1時

內存:

a | b

LayoutKind.Explicit

手動指定每個字段的內存偏移

[StructLayout(LayoutKind.Explicit)]
struct A
{
    [FieldOffset(0)] public byte a;
    [FieldOffset(4)] public int b;
}

內存:

a | pad | pad | pad | b

 LayoutKind.Auto

CLR 自己決定字段順序和對齊

[StructLayout(LayoutKind.Auto)]
struct A
{
    public byte a;
    public int b;
}

JIT 可以隨時改變佈局, 僅限純託管代碼使用。

LayoutKind

順序可控

可用於 C++ 互操作

使用頻率

Sequential



⭐⭐⭐⭐⭐

Explicit

✅(手動)


⭐⭐

Auto




Pack 和 LayoutKind 配套使用

Pack 控制“對齊粒度”

Pack

對齊粒度

常見用途

1

1 字節

SDK / 網絡 / 文件 / PInvoke

2

2 字節

老協議

4

4 字節

Win32 API

8

8 字節

CLR 默認

16

16 字節

SIMD(少見)

Pack 必須和 C++ 的 #pragma pack

一個你很可能會遇到的真實例子

C++

#pragma pack(push, 1)
struct Data
{
    uint8_t flag;
    int32_t value;
};
#pragma pack(pop)

C# 錯誤

[StructLayout(LayoutKind.Sequential)]
struct Data
{
    public byte flag;
    public int value;
}

value 會被對齊到 4 字節,錯 3 個字節

正確:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Data
{
    public byte flag;
    public int value;
}

Pack = 1 = “按字節對齊,不補空隙”

【必須】MarshalAs 的類型(⭐重點)

數組(最常見)

固定數組(結構體字段)

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public int[] values;

C++:

int32_t values[8];

沒有 MarshalAs → 變成指針

字符數組(C 風格字符串)

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string name;

C++:

char name[32];

string(非常重要)

ANSI 字符串

[MarshalAs(UnmanagedType.LPStr)]
string text;

Unicode 字符串

[MarshalAs(UnmanagedType.LPWStr)]
string text;

不寫的話默認行為經常不符合 C++ 期望

bool(極易出錯)

錯誤直覺

public bool flag; // ❌

正確(C++ bool = 1 byte)

[MarshalAs(UnmanagedType.I1)]
public bool flag;

C++:

bool flag; // 1 byte

enum(強烈建議指定)

public enum Status : int
{
    OK = 0,
    NG = 1
}

明確指定底層類型,不用 MarshalAs,但 必須指定 : int

一個完整正確示例

類型

是否 MarshalAs

long


double


ROIStruct_t[]

✅ ByValArray

bool

✅ I1

string

✅ LPStr / LPWStr

enum

❌(但指定底層類型)

C#

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CameraParam
{
    public int width;
    public int height;

    [MarshalAs(UnmanagedType.I1)]
    public bool enable;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public ROIStruct_t[] rois;
}

C++

#pragma pack(push, 1)
struct CameraParam
{
    int32_t width;
    int32_t height;
    bool enable;
    ROIStruct_t rois[8];
};
#pragma pack(pop)