在C#中,我编写
unsafe void Main() {
float f = 3.14f;
int i = *(int*)&f;
}
有可能把这个代码翻译成F#吗?
我的理解是,指针由nativeptr<'a>
类型表示,但我一生都找不到地址引用&
和指针取消引用*
运算符的等价物。我怀疑NativePtr.get
可能是后者,但我无法理解它的实现,因为我对IL的理解不够好。
我知道BitConverter
和Marshal
,但我正在寻找一种在不复制内存的情况下实现位篡改的方法。
NativePtr.get
和set
函数以偏移量进行读取和写入。如果你需要一个字节一个字节地读取,那么就使用它。如果您需要在偏移量为零的情况下读取,可以使用read
和write
,它们的性能会稍高一些。
用于获取"原始"地址(与byref<_>
引用相反)的运算符称为&&
(参见定义)。
但还涉及一些技巧,例如:在获取变量mutable
的地址之前,您需要标记它,您不能只存储nativeptr<_>
值,您需要将其转换为nativeint
,再加上nativeptr<_>
值是强类型的,因此您需要通过nativeint
在它们之间进行转换,等等。
下面的代码片段将与您的C#代码等效(一步一步,并使用完整的类型注释以提高清晰度):
open FSharp.NativeInterop
let Main() =
let mutable x: float = 3.1415
let floatPtr: nativeint = NativePtr.toNativeInt<float> &&x
let intPtr: nativeptr<int> = floatPtr |> NativePtr.ofNativeInt<int>
let asInt: int = NativeInterop.NativePtr.read intPtr
asInt
或者更紧凑的版本:
open FSharp.NativeInterop
let Main() =
let mutable x = 3.1415
&&x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<int>
或者将其打包以供重复使用:
// val inline readAs : x:'a -> 'b when 'a : unmanaged and 'b : unmanaged
let inline readAs (x: 'a) : 'b =
let mutable x' = x
&&x' |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<'b>
let Main() =
let i = readAs 3.1415 : int
()
说了这么多,我完全同意John Palmer和GuyCoder的观点:如果可能的话,请不要这样做。这看起来正是Knuth博士警告我们的那种过早的优化。