🌐
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.
🌐
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
🌐
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.
🌐
Ericchiang
ericchiang.github.io › post › cgo
Eric Chiang | Calling C from Go
February 17, 2024 - This post covers cgo, Go’s C interoperability layer.
🌐
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?

🌐
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.
🌐
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.
🌐
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.
Find elsewhere
🌐
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.
🌐
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%
🌐
YouTube
youtube.com › watch
How to use the Go language's cgo package to interface with C - YouTube
The Go programming language is normally used with its own libraries and packages, but it can interface with the world of C libraries and programs, too. Learn...
Published   August 29, 2023
🌐
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.
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".

🌐
GitHub
github.com › vladimirvivien › go-cshared-examples
GitHub - vladimirvivien/go-cshared-examples: Calling Go Functions from Other Languages using C Shared Libraries · GitHub
Calling Go Functions from Other Languages using C Shared Libraries - vladimirvivien/go-cshared-examples
Starred by 953 users
Forked by 113 users
Languages   Dart 13.6% | Python 12.6% | Zig 12.1% | C 11.0% | Java 10.8% | Cython 9.9%
🌐
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.
🌐
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.
🌐
Coding Explorations
codingexplorations.com › blog › unraveling-cgo-bridging-the-gap-between-go-and-c
Unraveling CGo: Bridging the Gap Between Go and C — Coding Explorations
August 30, 2023 - CGo is a feature of the Go programming language that enables Go programs to call C code and vice versa. By using CGo, developers can integrate functionality written in C directly into their Go applications, providing an interoperable bridge ...