最近仕事の都合でgo言語とc言語の相互呼び出しを実装する必要がありました。 Go 言語と C 言語は密接な関係にあるため、両者間の呼び出しは言語レベルで実現できます。以下にその概要を示します。
go 言語は C 言語を呼び出します
以下は簡単な例です:
package main // #include <stdio.h> // #include <stdlib.h> /* void print(char *str) { printf("%s\n", str); } */ import "C" import "unsafe" func main() { s := "Hello Cgo" cs := C.CString(s) C.print(cs) C.free(unsafe.pointer(cs)) }
「通常の」go コードと比較すると、上記のコードにはいくつかの「特別な」機能があります。
- #C 言語ヘッダー ファイルのインクルード ワードが冒頭のコメントに表示されます。
- C 言語の関数 print# がコメント # 内に定義されています。 ##C
- という名前の「パッケージ」をインポートしました
#上で定義した C 言語関数 print が main 関数で呼び出されました
まず第一に、上記のインクルード ヘッダー ファイルと print 関数定義と同様に、go ソース ファイル内の C 言語コードをコメントで囲む必要があります。次に、import "C" ステートメントが必要ですが、これを行うことはできません。上記の C コードとは空行で区切られており、密接に接続されている必要があります。ここでの「C」はパッケージ名ではなく、名前空間に似た概念、または疑似パッケージとして理解できます。C 言語のすべての構文要素はこの疑似パッケージの下にあり、最後に、 c 構文要素はその前にある必要があります。上記のコードに C.uint や C.print、C.free などの疑似パッケージ プレフィックスを追加します。
Go で C 言語を呼び出す詳しい使い方については、Go と C 言語の相互運用性を参照してください。この記事では、一つ一つ詳しく説明しません。
上記の例では、C 言語が go コードに埋め込まれていますが、コードがより大きくて複雑な場合、これは明らかに非常にプロフェッショナルではありません。では、C 言語のコードを Go のコードから分離して個別に定義することはできるのでしょうか?答えは「はい」です。共有ライブラリを通じて実現できます。
cgo は、コンパイル後に go ソース コードがどの共有ライブラリにリンクされるかを指定する #cgo
インジケーターを提供します。例は次のとおりです。
// hello.go package main // #cgo LDFLAGS: -L ./ -lhello // #include <stdio.h> // #include <stdlib.h> // #include "hello.h" import "C" func main() { C.hello() } // hello.c #include "hello.h" void hello() { printf("hello, go\n"); } // hello.h extern void hello();
hello.go で、コンパイルに使用される #cgo
インジケーターの後に LDFLAGS: -L ./ -lhello
を追加します。 go コード の場合、カレントディレクトリ内の so ライブラリを検索してリンクすることを指定します。
したがって、hello.c をダイナミック ライブラリにコンパイルしてから go コードをコンパイルするだけで、go コードの実行時に共有ライブラリ内の C 言語関数を呼び出すことができます。手順は次のとおりです。
gcc -fPIC -o libhello.so hello.c
go build -o hello
./hello
c 言語呼び出し go 言語
and in go で C ソースコードを呼び出すのに比べて、c で go 関数を使用する機会は少なくなります。なぜなら、一般的に、高級言語を低級言語を呼び出すための接着剤として使用すると、それぞれの特性を最大限に活かすことができますが、低級言語を使用して高級言語を呼び出すと、低レベル言語のパフォーマンス上の利点 go では、「エクスポート関数名」を使用できます C コードで使用するために go 関数をエクスポートするには、簡単な例を見てください:
// hello.go package main import "C" import "fmt" // export Go2C func Go2C() { fmt.Println("hello, C") }
は go コードを次の形式にコンパイルできますgo build
のコンパイル オプションを通じて呼び出す C コード用の共有ライブラリ。 so ライブラリをコンパイルするときは、main 関数と main 関数が存在する必要があることに注意してください (main 関数が空の場合でも)。コンパイル手順は次のとおりです: go build -v -x -buildmode=c-shared -o libhello.so hello.go
。
コンパイルが成功したら、新しく生成されたヘッダー ファイルを C コードに導入し、コンパイル中にダイナミック ライブラリをリンクして go 関数の呼び出しを実現するだけです。コードは次のとおりです:
// hello.c #include <stdio.h> #include "libhello.h" int main() { Go2C(); return 0; }
これは、gcc -o hello -L. -lhello
を通じて実行可能プログラムにコンパイルできます。実行する前に、リンクする必要がある共有ライブラリが共有ライブラリのランタイム検索パスに存在することを確認する必要があることに注意してください。SO ライブラリのパスを /usr/lib に置くか、環境変数 LD_LIBRARY_PATH を変更できます。
概要
go 言語は、C コードを埋め込むか、共有ライブラリ関数を呼び出すことによって C 言語を呼び出すことができます。C 言語の go 関数呼び出しについては、go build# を通じて呼び出すことができます。 ##C コードで使用できるように go コードを共有ライブラリにコンパイルします。なお、この記事で紹介する共有ライブラリは全て動的共有ライブラリであり、静的共有ライブラリについては未テストなので興味のある方は実装してみてください。