2016-02-27 27 views
5

私はbinary.Write()関数を使用してデータをパックするbyte.Bufferを持っています。私は、このバイト配列をC関数に送る必要があります。 Go 1.6を使って私はこれを理解することに成功しませんでした。Go [] byteをC * charに変換する

buf := new(bytes.Buffer) //create my buffer 
.... 
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here 
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array 
rc := C.the_function(addr, C.int(buf.Len())) //Fails here 

それは言ってC関数を呼び出す行に失敗します。

panic: runtime error: cgo argument has Go pointer to Go pointer 

C関数:

int the_function(const void *data, int nbytes); 

私は仕事に以下を得ることができたが、それは間違っていると感じましたバイト配列を文字列に変換します。これを行うより良い方法はありますか?この方法はデータに副作用を与える危険性がありますか?

addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0])) 

これも厳密なcgoポインタルールを導入したGo 1.6で動作する必要があります。

ありがとうございます。

答えて

7

最初のアプローチを使用する場合は、関数呼び出し引数の外側にスライスを作成し、一時的に割り当てられたスライスヘッダーまたは引数の外側構造を避ける必要があります。cgo検査では、 Goに格納されたポインタ。

b := buf.Bytes() 
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len())) 

C.CString方法がより安全になりますが、その中のデータは、C・バッファにコピーされるので、メモリを行くことに何のポインタが存在しない、とbytes.Bufferの背後にあるスライスが変更される見込みはありませんかの外に出ます範囲。最初のバイトだけでなく、文字列全体を変換する必要があります。このメソッドは2回の割り当てとコピーが必要ですが、データ量が少ない場合はcgo呼び出しのオーバーヘッドに比べておそらく問題はありません。

str := buf.String() 
p := unsafe.Pointer(C.CString(str)) 
defer C.free(p) 
rc = C.the_function(p, C.int(len(str))) 

データの2つのコピーが、その溶液中で許容されない場合は、C自身をバッファリングし、そのバッファに単一のコピーを作成をmalloc第三の選択肢があります:

p := C.malloc(C.size_t(len(b))) 
defer C.free(p) 

// copy the data into the buffer, by converting it to a Go array 
cBuf := (*[1 << 30]byte)(p) 
copy(cBuf[:], b) 
rc = C.the_function(p, C.int(buf.Len())) 

しかし後者の両方のオプションでは、mallocされたポインタを解放することを忘れないでください。

+0

[]バイトをC文字列に変換するのは良い考えではありません。 []バイトの '\ 0'はc文字列を表し、c文字列の長さはorigin []バイトの長さと等しくない可能性があります。 –

+0

@bronzeman:明らかに問題の関数は引数としてバッファの長さをとり、NULL終了文字列を期待していません。 'C.CString'は必要に応じてヌルバイトを追加しますが、正確な文字列長を渡すことでヌルバイトをスキップします。 – JimB

0

Cにポインタを渡すルールがgo1.6で変更されたため、プログラムがクラッシュする(詳細はhttps://tip.golang.org/doc/go1.6#cgoを参照)。

私はあなたのプログラムがクラッシュする理由を知らないので、Go issue https://github.com/golang/go/issues/14546を作成しました。

しかし、問題の答えにかかわらず、私はバイトの内部ビットを使用しません。あなたはcgoに直接渡すためにバッファします。 bytes.Bufferの実装は将来変更される可能性があり、プログラムは不思議に壊れ始めるでしょう。私はあなたが必要とするデータを適切な構造にコピーして、それをcgoに渡すだけです。

アレックス

+0

OPはバイトの内部ビットを使用していません。理由を説明しましたが、あなたの問題のdupもそうです。バッファによって返されたスライスの使用には何も問題ありません – JimB

関連する問題