(* C.uchar)(&buffer [0])与(* C.uchar)(unsafe.Pointer(&buffer [0]))

时间:2018-07-17 14:42:11

标签: c go unsafe-pointers

我们在这里讨论使用(或不使用)<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/linearLay" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="40dp" android:layout_weight="1" android:text="TextView" /> 从Go到C传递指向字节数组的指针。

(不)使用unsafe.Pointer的最大原因是什么?我将一致性作为一个理由,因为即使您使用另一种语言,您也会调用“外部”函数,并且您想保证其指针类型。

但是,由于Go语言的样式看上去有点像C,因此使用unsafe.Pointer()进行直接转换是有效的 。它有效的事实并不能使我确信它比使用(*C.uchar)(&buffer[0]))更安全。

也许Go强制转换看起来像函数调用,让我有些困惑/矛盾,并且unsafe.Pointer()被定义为Pointer实际上type Pointer *ArbitraryType不是实际上没有调用任何命令来执行“转换”,但实际上只是在功能上解释了正在发生的事情,就像宏可以做的那样。< / p>

1 个答案:

答案 0 :(得分:2)

Go文档中满是警告,禁止使用软件包unsafe不必要地破坏Go的类型系统。明确将unsafe.Pointer确定为不安全。


  

Package unsafe

     

不安全包装中包含的操作会绕过类型安全   进入程序。

     

进口不安全的包裹可能是不可携带的,也不受保护   遵循Go 1兼容性指南。

     

type Pointer

     

因此,指针允许程序破坏类型系统并读取   并写入任意内存。使用时应格外小心。

     
     

Command cgo

     

Passing pointers

     

Go是一种垃圾收集语言,垃圾收集器需要   知道Go内存中每个指针的位置。因为这,   在Go和C之间传递指针受到限制。

     

[这些]规则在运行时动态检查。

     

使用不安全的软件包可能会破坏这种实施,   当然没有什么可以阻止C代码做任何事情   它喜欢。但是,违反这些规则的程序可能会失败   以意想不到的和不可预测的方式。


让我们回顾一个现实的例子。

这是一个接收字节缓冲区的C函数。

void printbuf(size_t len, unsigned char *buf)

在Go中,使用cgo并保留匹配类型的类型安全性,我们可以编写

var buf []byte

C.printbuf(C.size_t(len(buf)), (*C.uchar)(&buf[0]))

但是,这仍然不安全,如果buf[0]len(buf) == 0将超出范围。将buf初始化为零值时,数组指针也将为nil。我们可以将完整性检查巧妙地封装在Go函数中,Go gc优化编译器将内联Go函数。

func cbuf(buf []byte) (size C.size_t, ptr *C.uchar) {
    var bufptr *byte
    if cap(buf) > 0 {
        bufptr = &(buf[:1][0])
    }
    return C.size_t(len(buf)), (*C.uchar)(bufptr)
}

bufsize, bufptr := cbuf(buf)
C.printbuf(bufsize, bufptr)

使用unsafe.Pointer对类型系统进行排污是不安全的。例如,

C.printbuf(C.size_t(len(buf)), (*C.uchar)(unsafe.Pointer(&buf[0])))

buf类型可以是任何索引类型:数组,指向数组的指针,切片,字符串或映射。更糟糕的是,如果不是一个字节,那么大小将是错误的。现在变得非常难看,

C.printbuf(C.size_t(len(buf)*int(unsafe.Sizeof(buf[0]))), (*C.uchar)(unsafe.Pointer(&buf[0])))

我们还没有考虑nil指针和超出范围的值。

接下来的代码审查:代码应该正确,可维护,健壮,合理高效,并且最重要的是可读性。不要指望unsafe.Pointer用法的代码审查会顺利进行。


让我们听听您使用unsafe.Pointer的理由。


示例代码

printbuf.go

package main

/*
#include <stdio.h>

void printbuf(size_t len, unsigned char *buf) {
    printf("%lu [", len);
    if (!buf) {
        len = 0;
    }
    size_t maxwidth = 16;
    size_t width = len <= maxwidth ? len : maxwidth;
    for (size_t i = 0; i < width; i++) {
        if (i > 0) {
            printf(" ");
        }
        printf("%02X", buf[i]);
    }
    if (width < len) {
        printf(" ...");
    }
    printf("]\n");
}
*/
import "C"

import (
    "unsafe"
)

// NOTE: -gcflags='-m' : can inline cbuf : inlining call to cbuf
func cbuf(buf []byte) (size C.size_t, ptr *C.uchar) {
    var bufptr *byte
    if cap(buf) > 0 {
        bufptr = &(buf[:1][0])
    }
    return C.size_t(len(buf)), (*C.uchar)(bufptr)
}

func main() {

    var buf []byte // zero-value = nil, len = 0, cap = 0

    bufsize, bufptr := cbuf(buf)
    C.printbuf(bufsize, bufptr)

    buf = make([]byte, 0) // len = 0, cap = 0

    bufsize, bufptr = cbuf(buf)
    C.printbuf(bufsize, bufptr)

    buf = make([]byte, 0, 32) // len = 0

    bufsize, bufptr = cbuf(buf)
    C.printbuf(bufsize, bufptr)

    buf = make([]byte, 32) // len > 0
    for i := range buf {
        buf[i] = byte(i)
    }

    bufsize, bufptr = cbuf(buf)
    C.printbuf(bufsize, bufptr)

    if len(buf) > 0 {

        C.printbuf(C.size_t(len(buf)), (*C.uchar)(&buf[0]))

        C.printbuf(C.size_t(len(buf)), (*C.uchar)(unsafe.Pointer(&buf[0])))

        C.printbuf(C.size_t(len(buf)*int(unsafe.Sizeof(buf[0]))), (*C.uchar)(unsafe.Pointer(&buf[0])))

    }

}

输出:

0 []
0 []
0 []
32 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]
32 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]
32 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]
32 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]