登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> CLQ工作室开源代码 >> 主题: golang 中如何访问 c 语言风格的字节流缓冲区     [回主站]     [分站链接]
标题
golang 中如何访问 c 语言风格的字节流缓冲区
clq
浏览(399) + 2023-01-25 14:15:32 发表 编辑

关键字:

[2023-01-25 14:17:22 最后更新]
golang 中如何访问 c 语言风格的字节流缓冲区

在 c 语言或者是 delphi 这样的原生语言中字节缓冲区是很常见的。例如 buf = malloc(1024);

但在脚本语言中基本上不会有。所以 nodejs 中特意扩展了一个专用的类,而 java 中是用的流类。
在 golang 中是怎样呢? golang 中也有类似流的东西,但在与 C 语言的函数直接操作时会很困难,所以很多库直接用的 cgo 。

但如果是纯 golang 直接调用 dll 或者是读取二进制文件应该怎么处理呢? 我没仔细找官方推荐的方法,不知道有没有,不过通过对 golang 操作 dll 和读取二进制文件的方法可以知道以下的方法是可靠的。

即用 []byte 来“代替” char * buf 这样的 c 语言表达式。实际上这并不是“代替”而是它就等于 c 语言中的 char * ,只不过在访问时需要一些技巧.

读取二进制的代码如下

s := make([]byte, 4096);

off := 1024;

//f.Seek(off, 0); //SEEK_SET = 0
f.Seek(int64(off), os.SEEK_SET); //SEEK_SET = 0

//nr, err := f.Read(s[:]); //可怕的语法 //尽量用 function_filebuf.go

//slice[:] 应该表示的切片本身,所以不用应该是一样的效果
nr, err := f.Read(s);

看到这里时其实还有疑虑,这 []byte 里对应的物理内存是怎样的,怎样取得 char * 这样的指针地址呢?这个 []byte 里的内存块是连续的吗,还只是一个列表?
实际上这 []byte 就是一块连续的内存,通过 "&s[0]" 就可以取得它的内存块地址。这个语法其实更接近取 delphi 字符串的内存块地址。
首先 "s[0]" 表示的是内存块的第一个字符,而 "&" 在 golang 和 c 语言中是一样的,都是取内存地址,
所以取第一个字符(字节)的地址就是整个内存块的地址了,当然前提是 []byte 中不是元素列表而是整块原始的物理内存。

这其实很好证明,只要找一个 golang 调用字符串相关的 api dll 调用你就会知道了,golang 是先将字符串转换成 u16 的 []byte 然后再取的 "&s[0]" 传递给 dll 的函数的。
而相关的 unsafe 什么什么的一堆操作函数不过是在强制转换指针类型罢了。

以下是我直接调用 gtk 的 dll 的示例,如果传递的 appid 不对的话,gtk 是会报 appid 为 null 或者不符合规范的。

```
appid := "org.gtk.example\x00";
buf := []byte(appid);
p := &buf[0];

r := unsafe.Pointer(p);
r2 := uintptr(r);
app,_,_ := gtk_application_new2.Call(r2, IntPtr(0));

```

这里要注意的是 r2 的结果是会被 gc 回收的,所以不能在函数外先转换再在这里用。如果想用一个函数封装的话,它的返回值是不能为 r2 的,而应该是包含 r2 和原始 []byte 结果的结构体才能避免 gc 。
我思考了好多年后才发现这一方便的处理方式,之前一直是苦记 golang 的释放规则和语法规则。实现上可以轻松简单的封装成以下结构体和转换函数

```
//避免 gc 回收
type cstring struct {
s string;
buf []byte;

c_str uintptr; //最后的 c 语言 dll 导出函数中需要的 char * 指针

}//

var g_id = to_c_str("org.gtk.example\x00");

func to_c_str(s string) cstring {
//return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)));

var r cstring;

r.buf = []byte(s);

p := &r.buf[0];
//p := &buf;

//&StringToUTF16(s)[0]

//return p;

_r := unsafe.Pointer(p);

r.c_str = uintptr(_r);

return r;
}//

```

这时候,调用代码就变成了

app,_,_ := gtk_application_new2.Call(g_id.c_str, IntPtr(0));

而网上流传的以下两个函数中

```
func IntPtr(n int) uintptr {
return uintptr(n);
}//

func StrPtr(s string) uintptr {
return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)));
}//

```

StrPtr 的结果肯定是不安全的,会在某个意外的时间点被回收导致程序莫名的崩溃。这我在多年前将 golang 封装成 http 的服务器 dll 中碰到了,一直在某个访问量大的时候崩溃,后来才发现是这个原因。

其实这在有 gc 的语言中,比如 java c# 中都存在,我目前在其他语言中的办法是维护一个 map 将生成的指针(语言中一般叫引用)都保存一份,在不需要时才手工从 map 中删除这样就避免了 gc 。






总数:0 页次:1/0 首页 尾页  
总数:0 页次:1/0 首页 尾页  


所在合集/目录
golang 更多



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1