将* C.uint8_t转换为[] uint8是否安全?

时间:2018-11-28 03:03:13

标签: c go cgo

我试图将uint8_t数组从C代码传递到Go,然后从文件中读取一些字节,并将它们存储在Go代码中。

示例代码在这里。

main.c:

#define BUFFER_SIZE 16384

int read_go(uint8_t* buffer, int bufferSize);

void read_buf() {
     uint8_t* buffer = (uint8_t*)malloc(BUFFER_SIZE);
     read_go(buffer, BUFFER_SIZE)

     // do something with buffer

     free(buffer)
}

read.go:

 /*
 #include "main.c"

 extern int read_go(uint8_t* buffer, int bufferSize);
 */
 import "C"

 //export read_go
 func read_go(buffer *C.uint8_t, bufferSize C.int) C.int {
      f, _ := os.Open("filename")
      defer f.close()

      buff := *(*[]uint8)(unsafe.Pointer(&buffer))
      n, _ := f.Read(buff)

      return C.int(n)
 }

效果很好,但我担心分段错误。因为与C的读取功能不同,我无法在读取功能中指定缓冲区大小。

我知道Read函数从文件读取的字节与len(buff)一样多。但是我不能保证len(buff)与bufferSize相同。

将* C.uint8_t转换为[] uint8是否安全?

1 个答案:

答案 0 :(得分:4)

您的代码错误。

Go切片被实现为Go struct

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

您对slice.lenslice.cap的值未定义。


  

寻求调试帮助的问题(“为什么此代码不起作用?”)必须   包括所需的行为,特定的问题或错误以及   在问题本身中重现它所需的最短代码。

您的代码片段无法编译且不完整。


buf是满足byte的Go io.Reader切片。

type Reader interface {
    Read(p []byte) (n int, err error)
}

buffer *C.uint8_t
bufferSize C.int

buf := (*[1 << 30]byte)(unsafe.Pointer(buffer))[:bufferSize:bufferSize]
n, err := f.Read(buf)

这是我可复制的解决方案。

main.c

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFFER_SIZE 16384

int read_go(uint8_t* buffer, int bufferSize);

void read_buf() {
    uint8_t* buffer = (uint8_t*)malloc(BUFFER_SIZE+1);
    buffer[BUFFER_SIZE]='\0';
    int n = read_go(buffer, BUFFER_SIZE);
    if (n < 0) {
        printf("read_go: error: %d\n", n);
        n = 0;
    }
    if (n > BUFFER_SIZE) {
        n = BUFFER_SIZE;
    }
    buffer[n] = '\0';

    // do something with buffer
    int width = n;
    printf("%d\n", width);
    if (width > 16) {
        width = 16;
    }
    for (int i = 0; i < width; i++)     {
        printf("%02X", buffer[i]);
    }
    printf("\n");
    for (int i = 0; i < width; i++)     {
        printf("%-2c", buffer[i]);
    }
    printf("\n");

    free(buffer);
}

int main(int argc, char *argv[]) {
    read_buf();
    return EXIT_SUCCESS;
}

read.go

package main

/*
#include <stdint.h>
*/
import "C"

import (
    "os"
    "unsafe"
)

//export read_go
func read_go(buffer *C.uint8_t, bufferSize C.int) C.int {
    f, err := os.Open("filename")
    if err != nil {
        return C.int(-1)
    }
    defer f.Close()

    buf := (*[1 << 30]byte)(unsafe.Pointer(buffer))[:bufferSize:bufferSize]
    n, err := f.Read(buf)
    if err != nil {
        return C.int(-1)
    }
    return C.int(n)
}

func main() {}

输出(Linux):

$ cat filename
filedata 01234567890
$ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
$ go build -a -o libread.so -buildmode=c-shared read.go
$ gcc main.c libread.so -o read && ./read
21
66696C65646174612030313233343536
f i l e d a t a   0 1 2 3 4 5 6 
$