🌐
Karthikkaranth
karthikkaranth.me › blog › calling-c-code-from-go
Calling C code from go | Karthik Karanth
The import "C" line allows us to interface with C code through our go program. The comments above it is actual C code that can be consumed by the rest of the code. We include stdlib.h so that we can call malloc and free; and greeter.h for our greeter function.
Discussions

can you interop cpp & go
You can interop any language with any other if they support c bindings, or more recently, WASM embedding. More on reddit.com
🌐 r/golang
7
0
July 23, 2023
Can C Code Be Ported To Go Quite Easily?
I have no clue what OSTEP means... Large parts if C should translate to Go, where things get interesting is when you need to deal with memory management. That will not translate that easily. More on reddit.com
🌐 r/golang
25
0
June 16, 2024
performance - Call C function from Golang - Stack Overflow
I want to write controllers logic and handle json and databases in Golang, while having my math processing model in C. In my opinion overhead calling C function have to be as low, as as setting reg... More on stackoverflow.com
🌐 stackoverflow.com
wrapper - How to use C++ in Go - Stack Overflow
There are limitations both to the interoperability and the implemented feature set of Go when using gccgo, however (e.g., limited goroutines, no garbage collection). More on stackoverflow.com
🌐 stackoverflow.com
🌐
GNU
gcc.gnu.org › onlinedocs › gcc-4.7.4 › gccgo › C-Type-Interoperability.html
C Type Interoperability - The GNU Go Compiler
Next: Function Names, Up: C Interoperability · Basic types map directly: an int in Go is an int in C, etc. Go byte is equivalent to C unsigned char. Pointers in Go are pointers in C. A Go struct is the same as C struct with the same field names and types. The Go string type is currently defined as a two-element structure: struct __go_string { const unsigned char *__data; int __length; }; You can't pass arrays between C and Go.
🌐
GitHub
github.com › hanselrd › go-c-interop
GitHub - hanselrd/go-c-interop: Simple example of mixing Go with C and vice versa
For this example I created a very simple math library that only exposes two functions: math_add and math_sub. I then call these two functions from Go using the cgo "C" module to bridge the gap between the two languages.
Author   hanselrd
🌐
Go Packages
pkg.go.dev › cmd › cgo
cgo command - cmd/cgo - Go Packages
April 7, 2026 - Cgo enables the creation of Go packages that call C code.
🌐
GNU
gcc.gnu.org › onlinedocs › gcc-7.5.0 › gccgo › C-Interoperability.html
The GNU Go Compiler: C Interoperability
When using gccgo there is limited interoperability with C, or with C++ code compiled using extern "C". This information is provided largely for documentation purposes. For ordinary use it is best to build programs with the go tool and then use import "C", as described at http://golang.org/cmd/cgo.
🌐
Ericchiang
ericchiang.github.io › post › cgo
Eric Chiang | Calling C from Go
February 17, 2024 - This post covers cgo, Go’s C interoperability layer.
Find elsewhere
🌐
GitHub
github.com › draffensperger › go-interlang
GitHub - draffensperger/go-interlang: Examples of calls between Go and C/C++ (and how to call a Go shared object from Node/Ruby/Python/Java) · GitHub
Examples of calls between Go and C/C++ and calling Go from dynamic languages.
Starred by 430 users
Forked by 63 users
Languages   Go 34.2% | C 24.5% | Shell 14.7% | Makefile 9.1% | C++ 6.5% | Ruby 6.0%
🌐
DEV Community
dev.to › metal3d › understand-how-to-use-c-libraries-in-go-with-cgo-3dbn
Understand how to use C libraries in Go, with CGO - DEV Community
October 9, 2023 - Go ease the work with many types, like array, pointers, and strings. When you need to send or receive a variable from or to C, the types are not exactly the same. So we need to "cast" types. But, no panic, after a while you will do it naturally. ... We need to modify the Go string to char* type.
🌐
GNU
gcc.gnu.org › onlinedocs › gccgo › C-Interoperability.html
C Interoperability (The GNU Go Compiler)
When using gccgo there is limited interoperability with C, or with C++ code compiled using extern "C". This information is provided largely for documentation purposes. For ordinary use it is best to build programs with the go tool and then use import "C", as described at https://pkg.go.dev/cmd/cgo.
🌐
Medium
medium.com › mysterium-network › golang-c-interoperability-caf0ba9f7bf3
Golang — C++ interoperability - Mysterium Network
September 22, 2023 - There are other tools that can help with calling java or objective C code into Golang, but everything goes through an intermediary. At a fundamental level, there is interoperability between C and Golang.
🌐
Reddit
reddit.com › r/golang › can c code be ported to go quite easily?
r/golang on Reddit: Can C Code Be Ported To Go Quite Easily?
June 16, 2024 -

I’m teaching myself operating systems, doing so through OSTEP (it’s a book, acronym is common knowledge so I’m using without expansion). There are many C examples, and Python examples, and they help to solidify OS concepts.

One way I’m thinking I can take my learning a bit further, while also learning about some of Go’s important features, is porting the C and Python code over to Go.

But porting isn’t a trivial process. For example, porting C code to Ruby would be, and pardon my French, fucking horrible. But Go shares a lot of similarities with C, so I’m thinking maybe it could be an easier process, and maybe it could be a useful way to learn.

I also know some Go, enough to be productive but not enough to claim I can write production code well — Go is my hobby language. So given this information, and for those with the background I’m trying to develop, do you think what I am proposing would be worth the additional effort?

Top answer
1 of 1
25

In my opinion overhead calling C function have to be as low, as as setting registers rcx, rdx, rsi, rdi, doin some fastcall and getting out rax value. But i've heard of big overhead in cgo <…>

Your opinion is unfounded.
The reason calling from Go to C has noticeable overhead is due to the following reasons.

Let's first consider C

While not in any way required by the language, a typical C program compiled by a typical compiler and running on a typical OS as a regular process, heavily relies on the OS to carry out certain aspects of its runtime environment.
The supposedly most visible and important aspect is the stack: the kernel is responsible for setting it up after loading and initializing the program's image and before transferring execution to the entry point of the code of the newborn process.

Another crucial point is that, again, while not strictly required, most C programs rely on OS-native threads to implement multiple concurently executing flows through the program's code.

The function calls performed in the C code are typically compiled using the same ABI the target combination of the OS and hardware implement (unless, of course, the programmer had explicitly managed to tell the compiler to do otherwise—like, say, marking a specific function as having a different calling convention).

C has no automatic means of managing non-stack memory ("the heap").
Such management is typically done via the C's standard library functions of the malloc(3) family. These functions manage the heap and consider any memory allocated through them as "theirs" (which is quite logical).
C does not provide automatic garbage collection.

Let's recap: a typical program compiled from C: uses the OS-supplied threads and uses OS-supplied stacks in those threads; function calls most of the time follow the platform's ABI; heap memory is managed by a special library code; no GC.

Let's now consider Go

  • Any bit of Go code (both that of your program and of the runtime) runs in so-called goroutines which are like super light-weight threads.
  • The goroutine scheduler provided by the Go runtime (which is compiled/linked into any program written in Go) implements the so-called M×N scheduling of the goroutines—where M goroutines are multiplexed onto N OS-supplied threads, where M is typically way higher than N.
  • Function calls in Go do not follow the target platform's ABI.
    Specifically, AFAIK contemporary versions of Go pass all call arguments on the stack¹.
  • A goroutine is always running on an OS-provided thread.
    A goroutine which is waiting on some resource managed by the Go runtime (such as an operation on a channel, a timer, a network socket etc) does not occupy an OS thread.
    When the scheduler selects a goroutine for execution, it has to assign it to a free OS thread which is in the possession of the Go runtime; while the scheduler tries hard to place the goroutine onto the same thread it was executing on before being suspended, that not always succeeds, and so goroutines may freely migrate between different OS threads.

The points above naturally lead to goroutines having their own stacks which are completely independent of those provided by the OS for its threads.

The heap memory is managed by the Go runtime, automatically, and its done directly, no C stdlib is used for this.

Go has GC, and this GC is concurrent in that it runs completely concurrently with the goroutines executing the program's code.

The stacks used by goroutines are allocated on the heap using the memory manager provided by the Go runtime. Unlike C, these stacks are reallocatable².

Let's recap: goroutines have their own stacks, use calling convention not compatible with neither the platform's ABI nor that of C, and may be executing on different OS threads at different points of their execution.
The Go runtime manages the heap memory directly (this includes the stacks of the goroutines) and has a fully-concurrent GC.

Let's now consider calls from Go to C

As you should supposedly see by now, the "worlds" of runtime environments in which the Go and C code runs are different enough to have big "impedance mismatch" which requires certain gatewaying when doing FFI—with non-zero cost.

In particular, when the Go code is about to call into C, the following must be done:

  1. The goroutine must be locked to the OS thread it's currently running on ("pinned").
  2. Since the target C call must be done according to the platform's ABI, the current execution context must be saved—at least those registers which will be trashed by the call.
  3. The cgo machinery must verify that any memory about to be passed to the target C call does not contain pointers to other memory blocks managed by Go, recursively—this is to allow the Go's GC to continue working concurrently.
  4. The execution must be switched from the goroutine stack to the thread's stack: a new stack frame must be created on the latter, and the parameters to the target C call must be placed there (and in the registers) according to the platform's ABI.
  5. The call is made.
  6. Upon return, the execution must be switched back to the goroutine's stack—again by gatewaying any returned results back to the stack frame of the executing goroutine.

As you could probably see, there are unavoidable costs, and placing values in some CPU registers is the most negligible of those costs.

What can be done about that

Generally, there are two vectors to attack the problem:

  • Make the calls to C infrequent.

    That is, if each call to C carries out lenghy CPU-intensive calculations, the overhead of performing these calls may be speculated to be dwarfed by the gains of making the computations performed by these calls faster.

  • Write critical functions in assembly.

    Go allows writing code directly in the assembly of the target H/W platform.

One "trick" which may allow you to get the best of both worlds is employing the ability of most industrial compilers to output the assembly language form of the function they compiled. So you may employ hard-core facilities provided by a C compiler such as auto-vectorisation (for SSEs) and aggressive optimisation, and then grab whatever it generated and wrap it in a thin layer of assembly which basically adapts the generated code to the native Go's ABI.

There's a host of 3rd-party Go packages which do this (say, this and that) and obviously the Go runtime does this as well.


¹ Since 1.17 Go is progressively switching to using register-based calling convention.
I have no information on whether this makes Go code compiled for particular GOOS/GOARCH combos to follow their native ABIs or not.
Go 1.18 implements register calling convention on all supported OSes when compiled for 64-bit CPUs (or CPU modes).

² Before 1.4 goroutine stacks had even more interesting design: they could consist of multiple segments forming a linked list; when a stack wanted to grow beyond its current size, a new segment was allocated and linked to the last one. This was called "split stacks".

🌐
Leapcell
leapcell.io › blog › go-and-c-interoperability-understanding-cgo
Go and C Interoperability Understanding cgo | Leapcell
September 11, 2025 - This is precisely where cgo comes into play. cgo acts as Go's robust and essential bridge to the C programming language, allowing Go programs to seamlessly call C functions and C programs to call Go functions.
🌐
Xnacly
xnacly.me › posts › 2024 › go-cpp-interop
Calling Go Functions from c++ | xnacly - blog
July 20, 2024 - Very many go routines :^). There you have it, a nice and short explanation for calling go functions from c++ (if you need to).
🌐
SWIG
swig.org › Doc4.0 › Go.html
24 SWIG and Go
There are (at least) two different Go compilers. The first is the gc compiler of the Go distribution, normally invoked via the go tool. The second Go compiler is the gccgo compiler, which is a frontend to the GCC compiler suite. The interface to C/C++ code is completely different for the two Go compilers.
🌐
GitHub
github.com › vladimirvivien › go-cshared-examples
GitHub - vladimirvivien/go-cshared-examples: Calling Go Functions from Other Languages using C Shared Libraries
Calling Go Functions from Other Languages using C Shared Libraries - vladimirvivien/go-cshared-examples
Starred by 947 users
Forked by 114 users
Languages   Dart 13.6% | Python 12.6% | Zig 12.1% | C 11.0% | Java 10.8% | Cython 9.9% | Dart 13.6% | Python 12.6% | Zig 12.1% | C 11.0% | Java 10.8% | Cython 9.9%
🌐
Akrennmair
akrennmair.github.io › golang-cgo-slides
Go & cgo: integrating existing C code with Go
// Go string to C string; result must be freed with C.free func C.CString(string) *C.char // C string to Go string func C.GoString(*C.char) string // C string, length to Go string func C.GoStringN(*C.char, C.int) string // C pointer, length to Go []byte func C.GoBytes(unsafe.Pointer, C.int) []byte
Top answer
1 of 13
194

Update: I've succeeded in linking a small test C++ class with Go

If you wrap you C++ code with a C interface you should be able to call your library with cgo (see the example of gmp in $GOROOT/misc/cgo/gmp).

I'm not sure if the idea of a class in C++ is really expressible in Go, as it doesn't have inheritance.

Here's an example:

I have a C++ class defined as:

// foo.hpp
class cxxFoo {
public:
  int a;
  cxxFoo(int _a):a(_a){};
  ~cxxFoo(){};
  void Bar();
};

// foo.cpp
#include <iostream>
#include "foo.hpp"
void
cxxFoo::Bar(void){
  std::cout<<this->a<<std::endl;
}

which I want to use in Go. I'll use the C interface

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
  typedef void* Foo;
  Foo FooInit(void);
  void FooFree(Foo);
  void FooBar(Foo);
#ifdef __cplusplus
}
#endif

(I use a void* instead of a C struct so the compiler knows the size of Foo)

The implementation is:

//cfoo.cpp
#include "foo.hpp"
#include "foo.h"
Foo FooInit()
{
  cxxFoo * ret = new cxxFoo(1);
  return (void*)ret;
}
void FooFree(Foo f)
{
  cxxFoo * foo = (cxxFoo*)f;
  delete foo;
}
void FooBar(Foo f)
{
  cxxFoo * foo = (cxxFoo*)f;
  foo->Bar();
}

with all that done, the Go file is:

// foo.go
package foo
// #include "foo.h"
import "C"
import "unsafe"
type GoFoo struct {
     foo C.Foo;
}
func New()(GoFoo){
     var ret GoFoo;
     ret.foo = C.FooInit();
     return ret;
}
func (f GoFoo)Free(){
     C.FooFree(unsafe.Pointer(f.foo));
}
func (f GoFoo)Bar(){
     C.FooBar(unsafe.Pointer(f.foo));
}

The makefile I used to compile this was:

// makefile
TARG=foo
CGOFILES=foo.go
include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg
foo.o:foo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<
cfoo.o:cfoo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<
CGO_LDFLAGS+=-lstdc++
$(elem)_foo.so: foo.cgo4.o foo.o cfoo.o
    gcc $(_CGO_CFLAGS_$(GOARCH)) $(_CGO_LDFLAGS_$(GOOS)) -o $@ $^ $(CGO_LDFLAGS)

Try testing it with:

// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
    foo := New();
    foo.Bar();
    foo.Free();
}

You'll need to install the shared library with make install, then run make test. Expected output is:

gotest
rm -f _test/foo.a _gotest_.6
6g -o _gotest_.6 foo.cgo1.go foo.cgo2.go foo_test.go
rm -f _test/foo.a
gopack grc _test/foo.a _gotest_.6  foo.cgo3.6
1
PASS
2 of 13
68

Seems that currently SWIG is best solution for this:

https://www.swig.org/Doc4.0/Go.html

It supports inheritance and even allows to subclass C++ class with Go struct so when overridden methods are called in C++ code, Go code is fired.

Section about C++ in Go FAQ is updated and now mentions SWIG and no longer says "because Go is garbage-collected it will be unwise to do so, at least naively".