我们在这里讨论使用(或不使用)<?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>
答案 0 :(得分:2)
Go文档中满是警告,禁止使用软件包unsafe
不必要地破坏Go的类型系统。明确将unsafe.Pointer
确定为不安全。
不安全包装中包含的操作会绕过类型安全 进入程序。
进口不安全的包裹可能是不可携带的,也不受保护 遵循Go 1兼容性指南。
因此,指针允许程序破坏类型系统并读取 并写入任意内存。使用时应格外小心。
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 ...]