My problem is to send a structure between a program in C to a C# program.
I made a structure in C#:
public struct NetPoint {
public float lat; // 4 bytes
public float lon; // 4 bytes
public int alt; // 4 bytes
public long time; // 8 bytes
}
The total size of the structure must be 20 bytes.
When I do a sizeof()
in C++ of this structure,
System.Diagnostics.Debug.WriteLine(
"SizeOf(NetPoint) = " +
System开发者_运维技巧.Runtime.InteropServices.Marshal.SizeOf(new NetPoint()));
the debug console shows:
SizeOf(NetPoint) = 24
But I expected to have 20 bytes. Why do I see a difference?
As a general rule, CPUs like to have variables aligned in memory at a location that is an even multiple of their size, so a four-byte integer should be on a memory address that is divisible by four, and an eight-byte long
should be at an address divisible by eight.
The C# (and C++) language designers know this, and they will insert padding in structures to provide the necessary alignment. So the actual layout of your structure looks like this:
public struct NetPoint {
public float lat; // 4 bytes Offset 0
public float lon; // 4 bytes Offset 4
public int alt; // 4 bytes Offset 8
int to_preserve_alignment; // 4 bytes Offset 12
public long time; // 8 bytes Offset 16
}
You can fix this by making the long the first value, as a rule, if you always put the largest values at the beginning of your structures, you won't have any padding inserted to preserve alignment of members.
You can also fix it by adding
[StructLayout(LayoutKind.Sequential, Pack = 4)]
before the structure declaration, but that will result in mis-aligned long time
which hurts performance. On some CPUs it hurts performance quite a lot. (The ALPHA AXP would fault on misaligned members, for instance). x86 CPUs have only a minor performance penalty, but there is a danger of future CPUs having a major performance penalty, so it's best to design your structures to align properly (rather than packing them) if you can.
Actually, technically the structure must be a minimum of 20 bytes. If you allocate more when sending, the receiver just won't use / copy them. The problem is always underallocation.
That said, I see the problem. Hmm. I think the problem is the last long.... which IMHO gets aligned to eight bytes, injecting four empty bytes before. I think there is a performance penalty for having an eight-byte element not aligned to an eight-byte boundary.
Attach StructLayout attributes to determine the offset of every element manually. Then you should be able to get things in line.
Reference: How to control the physical layout of the data fields in the .NET Framework 2.0
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct NetPoint {
public float lat; // 4 bytes
public float lon; // 4 bytes
public int alt; // 4 bytes
public long time; // 8 bytes
}
That at least should align elements to a one-byte boundary. You can go further by defining the exact start of every element, too, if needed.
Try adding the attribute [StructLayout(LayoutKind.Sequential, Pack = 1)] and see what happens. I suspect an 8 byte padding, so it's 3x8 bytes.
TomTom answered this question pretty well I think, but there is another alternative if you end up in a tricky COM interop struct situation. Each field can be aligned on its own using FieldOffset attributes.
[StructLayout(LayoutKind.Explicit)]
public struct COMPoint
{
[FieldOffset(0)] public int X;
[FieldOffset(4)] public int Y;
}
精彩评论