C# Struct结构体里数组长度的指定
typedef struct Point{
unsigned short x;
unsigned short y;
}mPoint;//点坐标 typedef struct Line{
mPoint p[2];
unsigned char name[20];
unsigned int mark[5];
}mLine; //线坐标
如上一个C++的结构体Line,分别有3个数组
- 结构体数组
- 字节数组
- int数组
简单翻译成C#如下:
public struct Point{
public ushort x;
public ushort y;
};//点坐标 public struct Line{
Point[] p;
byte[] name;
uint[] mark;
}; //线坐标
但这样无法使用
这篇里的StructToBytes BytesToStruct等函数快捷转换字节用来作为和C++程序的通信。
MessageBox.Show(Marshal.SizeOf(typeof(Line)).ToString());
也是无法计算结构体长度的。
要解决这个问题,首先要看下字节对齐的概念
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。 现在已知32位机器上各种数据类型的长度如下:
char:1(有符号无符号同)
short:2(有符号无符号同)
int:4(有符号无符号同)
long:4(有符号无符号同)
float:4 double:8 编译器是按照什么样的原则进行对齐的? 先让我们看四个重要的基本概念:
1.数据类型自身的对齐值:
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
一般强制1字节对齐就好了,实现上很简单,在结构体上面加入
[StructLayout(LayoutKind.Sequential, Pack = 1)]
即可。
代码如下:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Point{
public ushort x;
public ushort y;
};//点坐标 [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Line{
Point[] p;
byte[] name;
uint[] mark;
}; //线坐标
但这样明显还不够,依然没指定数组的长度。
通过搜索资料最终尝试出的解决办法如下:
- 结构体数组
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.Struct)]
Point[] p;
注意:
MarshalAs属性指示如何在托管代码和非托管代码之间封送数据。
ByValArray
当 MarshalAsAttribute.Value 设置为 ByValArray 时,必须设置 SizeConst 以指示数组中的元素数。当需要区分字符串类型时,ArraySubType 字段可以选择包含数组元素的 UnmanagedType。此 UnmanagedType 只可用于作为结构中的字段的数组。 |
SizeConst = 2表示数组长度为2
ArraySubType = UnmanagedType.Struct 表示这个数组是Struct结构体数组
- 字节数组
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] name; //20
字节数组最简单了,同理
SizeConst = 20表示长度20字节
- int数组
int数组的解决办法网上似乎并没有,想了想上面 结构体数组 的解决办法后决定试一下。
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
public uint[] mark;
果然解决了
SizeConst = 5表示数组长度为5
ArraySubType = UnmanagedType.U4 表示数组内容是无符号4字节的整数,=uint类型 至此,几种数组全部搞定了,看下效果
using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Point{
public ushort x;
public ushort y;
};//点坐标 [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Line{
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.Struct)]
public Point[] p;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] name;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
public uint[] mark;
}; //线坐标
编译运行正常
MessageBox.Show(Marshal.SizeOf(typeof(Line)).ToString());
口算下长度
点:2 * 2(short) = 4字节
line里2个点即8字节
line里name长度20字节
line里5个uint数字 5* 4 = 20字节
20+20+8 = 48字节,长度正确
使用
这篇里的StructToBytes BytesToStruct等函数快捷转换字节测试,正常!
测试代码:
Point p;
p.x = 1; p.y = 2;
Point p2;
p2.x = 3; p2.y = 4;
uint[] uss = new uint[5];
uss[0] = 111;
uss[1] = 222;
uss[2] = 333;
uss[3] = 444;
uss[4] = 555; Line ll;
ll.mark = uss;
byte[] bb = new byte[20];
byte[] bb2 = Encoding.UTF8.GetBytes("测试");
Array.Copy(bb2, bb, bb2.Length);
ll.name = bb;
ll.p = new NetProtocol.Point1[] { p , p2 };
byte[] b = Common.StructToBytes(ll);
object oo = Common.BytesToStruct(b, typeof(Line));
NetProtocol.Line1 test2 = (Line)oo; string s = Encoding.UTF8.GetString(test2.name).Replace("\0", "");
两次转换后的字节和值都是正确的。
UnmanagedType 枚举
成员名称 说明
由 .NET Compact Framework 支持 AnsiBStr 长度前缀为单字节的 ANSI 字符串。可以在 String 数据类型上使用此成员。
由 .NET Compact Framework 支持 AsAny 一个动态类型,将在运行时确定对象的类型,并将该对象作为所确定的类型进行封送处理。仅对平台调用方法有效。
由 .NET Compact Framework 支持 Bool 4 字节布尔值(true != 0、false = 0)。这是 Win32 BOOL 类型。
由 .NET Compact Framework 支持 BStr 长度前缀为双字节的 Unicode 字符串。可以在 String 数据类型上使用此成员(它是 COM 中的默认字符串)。
由 .NET Compact Framework 支持 ByValArray 当 MarshalAsAttribute.Value 设置为 ByValArray 时,必须设置 SizeConst 以指示数组中的元素数。当需要区分字符串类型时,ArraySubType 字段可以选择包含数组元素的 UnmanagedType。此 UnmanagedType 只可用于作为结构中的字段的数组。
由 .NET Compact Framework 支持 ByValTStr 用于在结构中出现的内联定长字符数组。与 ByValTStr 一起使用的字符类型由应用于包含结构的 System.Runtime.InteropServices.StructLayoutAttribute 的 System.Runtime.InteropServices.CharSet 参数确定。应始终使用 MarshalAsAttribute.SizeConst 字段来指示数组的大小。
.NET Framework 的 ByValTStr 类型的行为类似于结构中的 C 样式、固定大小的字符串(例如,char s[5])。托管代码中的行为与 Microsoft Visual Basic 6.0 中的行为不同,后者不是空终止(例如,MyString As String * 5)。 由 .NET Compact Framework 支持 Currency 在 System.Decimal 上使用,以将十进制数值作为 COM 货币类型而不是 Decimal 封送。
由 .NET Compact Framework 支持 CustomMarshaler 当与 MarshalAsAttribute.MarshalType 或 MarshalAsAttribute.MarshalTypeRef 一起使用时,指定自定义封送拆收器类。MarshalAsAttribute.MarshalCookie 字段可用于将附加信息传递给自定义封送拆收器。可以在任何引用类型上使用此成员。
由 .NET Compact Framework 支持 Error 此与 I4 或 U4 关联的本机类型将导致参数作为导出类型库中的 HRESULT 导出。
由 .NET Compact Framework 支持 FunctionPtr 一个可用作 C 样式函数指针的整数。可将此成员用于 Delegate 数据类型或从 Delegate 继承的类型。
由 .NET Compact Framework 支持 I1 1 字节有符号整数。可使用此成员将布尔值转换为 1 字节、C 样式的 bool(true = 1、false = 0)。
由 .NET Compact Framework 支持 I2 2 字节有符号整数。
由 .NET Compact Framework 支持 I4 4 字节有符号整数。
由 .NET Compact Framework 支持 I8 8 字节有符号整数。
由 .NET Compact Framework 支持 IDispatch 一个 COM IDispatch 指针(在 Microsoft Visual Basic 6.0 中为 Object)。
由 .NET Compact Framework 支持 Interface COM 接口指针。从类元数据获得接口的 Guid。如果将此成员应用于类,则可以使用该成员指定确切的接口类型或默认的接口类型。当应用于 Object 数据类型时,此成员将产生 UnmanagedType.IUnknown 行为。
由 .NET Compact Framework 支持 IUnknown COMIUnknown 指针。可以在 Object 数据类型上使用此成员。
由 .NET Compact Framework 支持 LPArray 指向 C 样式数组的第一个元素的指针。当从托管到非托管进行封送处理时,该数组的长度由托管数组的长度确定。当从非托管到托管进行封送处理时,将根据 MarshalAsAttribute.SizeConst 和 MarshalAsAttribute.SizeParamIndex 字段确定该数组的长度,当需要区分字符串类型时,还可以后跟数组中元素的非托管类型。
由 .NET Compact Framework 支持 LPStr 单字节、空终止的 ANSI 字符串。可在 System.String 或 System.Text.StringBuilder 数据类型上使用此成员。
由 .NET Compact Framework 支持 LPStruct 一个指针,它指向用于封送托管格式化类的 C 样式结构。仅对平台调用方法有效。
由 .NET Compact Framework 支持 LPTStr 与平台相关的字符串:在 Windows 98 上为 ANSI,在 Windows NT 和 Windows XP 上为 Unicode。该值仅对平台调用受支持,而对 COM Interop 则不受支持,原因是不支持导出 LPTStr 类型的字符串。
由 .NET Compact Framework 支持 LPWStr 一个 2 字节、空终止的 Unicode 字符串。
请注意,如果非托管字符串不是使用非托管的 CoTaskMemAlloc 函数创建的,则不能在此非托管字符串中使用 LPWStr 值。 由 .NET Compact Framework 支持 R4 4 字节浮点数。
由 .NET Compact Framework 支持 R8 8 字节浮点数。
由 .NET Compact Framework 支持 SafeArray SafeArray 是自我描述的数组,它带有关联数组数据的类型、秩和界限。可将此成员与 MarshalAsAttribute.SafeArraySubType 字段一起使用,以重写默认元素类型。
由 .NET Compact Framework 支持 Struct 一个用于封送托管格式化类和值类型的 VARIANT。
由 .NET Compact Framework 支持 SysInt 与平台相关的有符号整数。在 32 位 Windows 上为 4 字节,在 64 位 Windows 上为 8 字节。
由 .NET Compact Framework 支持 SysUInt 与平台相关的无符号整数。在 32 位 Windows 上为 4 字节,在 64 位 Windows 上为 8 字节。
由 .NET Compact Framework 支持 TBStr 一个有长度前缀的与平台相关的 char 字符串。在 Windows 98 上为 ANSI,在 Windows NT 上为 Unicode。很少用到这个类似于 BSTR 的成员。
由 .NET Compact Framework 支持 U1 1 字节无符号整数。
由 .NET Compact Framework 支持 U2 2 字节无符号整数。
由 .NET Compact Framework 支持 U4 4 字节无符号整数。
由 .NET Compact Framework 支持 U8 8 字节无符号整数。
由 .NET Compact Framework 支持 VariantBool 2 字节、OLE 定义的 VARIANT_BOOL 类型(true = -1、false = 0)。
由 .NET Compact Framework 支持 VBByRefStr 允许 Visual Basic 2005 在非托管代码中更改字符串,并将结果在托管代码中反映出来。该值仅对平台调用受支持。
参考:
https://msdn.microsoft.com/zh-cn/magazine/system.runtime.interopservices.unmanagedtype(v=vs.80).aspx
http://blog.chinaunix.net/uid-14802518-id-2784907.html
C# Struct结构体里数组长度的指定的更多相关文章
- 读陈浩的《C语言结构体里的成员数组和指针》总结,零长度数组
原文链接:C语言结构体里的成员数组和指针 复制例如以下: 单看这文章的标题,你可能会认为好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Lar ...
- 1.0 基础、标示符、常量、数据类型(enum 枚举,struct 结构体)、操作符、循环、数组
一.程序 现实生活中,程序是指完成某些事务的一种既定方法和过程,可以把程序看成是一系列动作执行过程的描述. 在计算机世界,程序是指令,即为了让计算机执行某些操作或解决某个问题而编写的一系列有序指令的集 ...
- C语言结构体里的成员数组和指针
struct test{ int i; char *p; }; struct test *str; ; char *b = "ioiodddddddddddd"; str = (s ...
- C#学习笔记(七):结构体、数组、冒泡排序和调试
结构体 结构体不能重写默认无参构造函数 一位数组 using System; using System.Collections.Generic; using System.Linq; using Sy ...
- C# 篇基础知识2——运算符、类型转换、流程控制、枚举、结构体和数组、函数
1.运算符.类型转换 计算某年y某月m某日d是周几的基姆拉尔森公式公式:int week = (d + 2*m + 3*(m + 1)/5 + y + y/4 - y/100 + y/400 + 1) ...
- 通过 struct 成员地址 获取 struct 结构体地址
1. 问题描述: 现在定义了一个结构体: struct Foo { int a; int b; }; Foo foo; 假如由于函数传参等原因,现在程序只能拿到 foo.b 的地址,这时想通过某种方法 ...
- C++结构体对象数组的二进制方式读写
以一个学生信息的结构体数组为例. #include<iostream>#include<string>#include<fstream>using namespac ...
- C#语言struct结构体适用场景和注意事项
在C#语言中struct结构体和class之间的区别主要是值类型和引用类型的区别,但实际上如果使用不当是非常要命的.从Win32时代过来的人对于struct一点不感觉陌生,但是却反而忽略了一些基本问题 ...
- struct 结构体解析(原)
(一)基本概念 结构体是一个或是多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组合在一个名字之下.我们将关键字struct引入了结构声明中.结构声明包含在花括号内的一系列声明组成 ...
随机推荐
- Bundle包的制作与使用
一.清爽Bundle模式(在应用工程中创建Bundle的子文件夹,而非在Bundle项目中): 1.新建Bundle包 2.生成Bundle包,并拖入项目中,然后"右键显示包内容" ...
- Atitit. 项目文档目录大纲 总集合 v2
Atitit. 项目文档目录大纲 总集合 v2 -----Atitti.原有项目源码的架构,框架,配置与环境说明 v3 q511 -----Atitit.开发环境 与 工具 以及技术框架 以及 注意 ...
- Oracle/PLSQL: ORA-06550
参考: http://blog.csdn.net/haiross/article/details/20612135 Oracle/PLSQL: ORA-06550 Learn the cause an ...
- 异常处理之“The remote certificate is invalid according to the validation praocedure.”
参考文章:http://brainof-dave.blogspot.com.au/2008/08/remote-certificate-is-invalid-according.html 参考文章:h ...
- 安装phpstudy之后发现80端口被占用
安装phpstudy之后发现80端口被占用: 进入cmd的界面,在输入"netstat -ano",按回车键后,会显示当前电脑中程序占用的端口和程序ID等等信息:看到第一条就是:0 ...
- Android 复制文本内容到系统剪贴板的最简单实践
这个例子很简单,直接上截图和代码. 布局文件activity_copy.xml代码如下: <?xml version="1.0" encoding="utf-8&q ...
- 前端少侠的ps故事
前端少侠的ps故事 正所谓,码在江湖,身不由己.自21世纪前后端分离,代码分工细化以来,前端与设计的合作也变得越来越重要.有人说,如果前端懂设计的话,工作会更快一点.倘若说我入前端半年能算半个前端少侠 ...
- java实现单链表的整表创建
package com.java.dataStruct; public class Node<E> { E item; Node next; public Node(){ } public ...
- 第9章 Shell基础(3)_Bash的变量
4. Bash的变量 4.1 用户自定义变量 4.1.1 什么是变量 变量是计算机的内存单元,其中存放的值可以改变.当Shell脚本需要保存一些信息时,如一个文件名或是一个数字,就把它存放在一个变量中 ...
- jQuery.extend 函数详解
JQuery的extend扩展方法: Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,在此,我们一起去了解了解. 一.Jquery的扩展方 ...