Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Call go (or c) code from python with string as argument and string as return value

go code (foo.go) compiled into a shared library and a c header (foo.h) is generated.

For calling go from c, please see here


The code below shows two ways of passing string parameter to go function:

  1. Using GoString structure as argument, without making a copy in go code: no conversion to go string needed.
  2. Using c_char_p as argument, making a copy in go code when converting to go string.

When using the first method without the copy, I don't know how python will do the memory management with the pointer passed into go. So the second method is preferred.

Return Value

The go function returns a c string, and the reciving side is responsible for managing the lifecycle of the c string. It seems that python does not free it.

Code and Build Command

from ctypes import *
from ctypes import cdll
import time

class go_string(Structure):
    _fields_ = [
        ("p", c_char_p),
        ("n", c_int)]

lib = cdll.LoadLibrary('./')

def bar(str):
    b = go_string(c_char_p(str), len(str)) = c_char_p
    a =, c_char_p(str))
    print a



package main

import (

//export bar
func bar(a string, b *C.char) *C.char {
	c := a
	d := C.GoString(b)
	go func() {
		for {
			fmt.Println("a:", a)
			fmt.Println("b:", C.GoString(b))
			fmt.Println("c:", c)
			fmt.Println("d:", d)
	return C.CString("hello from go")

func main() {}


go build -buildmode=c-shared -o foo.go


Generated foo.h:

/* Created by "go tool cgo" - DO NOT EDIT. */

/* package command-line-arguments */

/* Start of preamble from import "C" comments.  */

/* End of preamble from import "C" comments.  */

/* Start of boilerplate cgo prologue.  */


typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;

  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;


/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {

extern char* bar(GoString p0, char* p1);

#ifdef __cplusplus
Copy link

wangkuiyi commented Apr 27, 2017

A digested version here:

The Go Source

// a.go
package main

// #include <string.h>
import "C"

//export add
func add(left, right int) int {
	return left + right

//export concat
func concat(left, right *C.char) *C.char {
	return C.CString(C.GoString(left) + C.GoString(right))

func main() {

To build it into a shared libarary:

go build -buildmode=c-shared -o a.go

The Python Code

import ctypes
lib = ctypes.cdll.LoadLibrary('./')
lib.concat.restype = ctypes.c_char_p
print(lib.concat("apple", "orange"))

Copy link

softarts commented Feb 18, 2020

need to free the buffer?

Copy link

hqnguyen99 commented Dec 11, 2020

Copy link

stackquest commented Feb 7, 2022

@wangkuiyi that code returns b'ao' instead appleorange.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment