level 13
Nukepayload2
楼主
在某些情况下我们需要强制转换某些结构体,比如为了查看整数 -1 的补码,要把它强制转换成无符号整数。为了看某单精度浮点数的IEEE 754编码,要把它转换为无符号整数。
同时,又不想去碰c++,那么你找对教程了!
我想到了几种强制转换的方式。
1.利用联合体。
显式指定结构体的字段偏移量并且让某些字段在内存中发生重叠可以让结构体成为联合体。
假如你要查看-1的补码
先定义这个结构体
Imports System.Runtime.InteropServices
...
<StructLayout(LayoutKind.Explicit)>
Structure I4U4
<FieldOffset(0)>
Dim int As Integer
<FieldOffset(0)>
Dim uint As UInteger
End Structure
然后新建这个类型的变量并向它的int变量赋值,从uint变量取出来的就是原封不动的补码。
可以直接用这个类型的变量计算从而避免辅助空间。
时间复杂度:O(1)
空间复杂度:0 到 O(1)
编写成本:
每种情况要定义不同的结构体,然后在该转换的变量上多次使用成员运算符。
编写成本最好时略高于用c#的强制转换,最差情况下需要反复编写结构体代码。
2.使用BitConverter
先用GetBytes转换成Byte()再转换成目标类型
参考源码是这样写的
转换:
public unsafe static byte[] GetBytes(int value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4)
byte[] bytes = new byte[4];
fixed(byte* b = bytes)
*((int*)b) = value;
return bytes;
}
变回:
public static unsafe int ToInt32 (byte[] value, int startIndex) {
if( value == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
}
if ((uint) startIndex >= value.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
}
if (startIndex > value.Length -4) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
Contract.EndContractBlock();
fixed( byte * pbyte = &value[startIndex]) {
if( startIndex % 4 == 0) { // data is aligned
return *((int *) pbyte);
}
else {
if( IsLittleEndian) {
return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
}
else {
return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
}
}
}
}
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要调用GetBytes()和Toxxxxx,灵活性不强,不能应对全部结构体。
因此编写成本低于或等于第一种。
3.使用MemoryStream
跟第二种差不多,写入内存流然后读出来。需要相应大小的数组作为辅助空间。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要辅助数组和内存流声明加上读写,同样不能应对全部的结构体,灵活性较差。
编写成本高于第二种低于等于第一种。
4.使用互操作函数
相当于把c#的不安全代码的转换翻译成vb。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:可以写成泛型函数或泛型类。调用时任何结构体都能轻易转换。
编写成本是很低的,比c#用指针转换写起来都省事。
5.使用支持指针的语言编写转换函数 (不推荐)
这明显是造轮子,写出来跟BitConverter效率差不多,为什么这样折腾呢?
时间复杂度:O(1)
空间复杂度:0
编写成本:调用与第4种方案类似。但是不能泛型。
编写成本大于等于第一种。
6.不做整数溢出检查并且使用CInt之类的运算符转换
在特殊情况比较好用,比如有符号与无符号的转换。但不能应对全部结构体。
时间复杂度:O(1)
空间复杂度:0
编写成本:跟c#的强制转换是一样的,有同样的适应性缺陷。
2015年11月28日 16点11分
1
同时,又不想去碰c++,那么你找对教程了!
我想到了几种强制转换的方式。
1.利用联合体。
显式指定结构体的字段偏移量并且让某些字段在内存中发生重叠可以让结构体成为联合体。
假如你要查看-1的补码
先定义这个结构体
Imports System.Runtime.InteropServices
...
<StructLayout(LayoutKind.Explicit)>
Structure I4U4
<FieldOffset(0)>
Dim int As Integer
<FieldOffset(0)>
Dim uint As UInteger
End Structure
然后新建这个类型的变量并向它的int变量赋值,从uint变量取出来的就是原封不动的补码。
可以直接用这个类型的变量计算从而避免辅助空间。
时间复杂度:O(1)
空间复杂度:0 到 O(1)
编写成本:
每种情况要定义不同的结构体,然后在该转换的变量上多次使用成员运算符。
编写成本最好时略高于用c#的强制转换,最差情况下需要反复编写结构体代码。
2.使用BitConverter
先用GetBytes转换成Byte()再转换成目标类型
参考源码是这样写的
转换:
public unsafe static byte[] GetBytes(int value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4)
byte[] bytes = new byte[4];
fixed(byte* b = bytes)
*((int*)b) = value;
return bytes;
}
变回:
public static unsafe int ToInt32 (byte[] value, int startIndex) {
if( value == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
}
if ((uint) startIndex >= value.Length) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
}
if (startIndex > value.Length -4) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
Contract.EndContractBlock();
fixed( byte * pbyte = &value[startIndex]) {
if( startIndex % 4 == 0) { // data is aligned
return *((int *) pbyte);
}
else {
if( IsLittleEndian) {
return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
}
else {
return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3));
}
}
}
}
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要调用GetBytes()和Toxxxxx,灵活性不强,不能应对全部结构体。
因此编写成本低于或等于第一种。
3.使用MemoryStream
跟第二种差不多,写入内存流然后读出来。需要相应大小的数组作为辅助空间。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:需要辅助数组和内存流声明加上读写,同样不能应对全部的结构体,灵活性较差。
编写成本高于第二种低于等于第一种。
4.使用互操作函数
相当于把c#的不安全代码的转换翻译成vb。
时间复杂度:O(1)
空间复杂度:O(1)
编写成本:可以写成泛型函数或泛型类。调用时任何结构体都能轻易转换。
编写成本是很低的,比c#用指针转换写起来都省事。
5.使用支持指针的语言编写转换函数 (不推荐)
这明显是造轮子,写出来跟BitConverter效率差不多,为什么这样折腾呢?
时间复杂度:O(1)
空间复杂度:0
编写成本:调用与第4种方案类似。但是不能泛型。
编写成本大于等于第一种。
6.不做整数溢出检查并且使用CInt之类的运算符转换
在特殊情况比较好用,比如有符号与无符号的转换。但不能应对全部结构体。
时间复杂度:O(1)
空间复杂度:0
编写成本:跟c#的强制转换是一样的,有同样的适应性缺陷。