百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Python ctypes详解(ctypes numpy)

itomcoil 2025-03-25 14:27 7 浏览

类型对应关系

1. 基础数据类型

ctypes 类型

C 类型

Python 类型

c_bool

_Bool(C99+)或 bool

bool

c_char

char

单字节字符串(长度1的 bytes)

c_wchar

wchar_t

单字符 Unicode 字符串

c_byte

signed char

int

c_ubyte

unsigned char

int

c_short

short

int

c_ushort

unsigned short

int

c_int

int

int

c_uint

unsigned int

int

c_long

long

int

c_ulong

unsigned long

int

c_longlong

long long(C99+)

int

c_ulonglong

unsigned long long

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float(具体精度依赖平台)

c_size_t

size_t

int

c_ssize_t

ssize_t

int

2.指针与字符串类型

ctypes 类型

C 类型

说明

c_char_p

char*

以 NUL 结尾的 C 字符串指针(字节流)

c_wchar_p

wchar_t*

宽字符字符串指针

c_void_p

void*

通用指针(地址)

POINTER(type)

type*

通用指针类型(需指定具体类型)

3.数组与复合类型

数组:通过 * 运算符定义:

  1. 如 c_int * 10 对应 C 的 int类型的数组,包含10个int类型;
  2. 如c_char_p * 10对应C的字符串类型的数组,包含10个char数组(c中的字符串用char数组表示)

结构体:继承 ctypes.Structure 并定义 _fields_ 字段列表:

class MyStruct(ctypes.Structure):  
    _fields_ = [("x", c_int), ("y", c_float)]  # 对应 C 的 `struct { int x; float y; }`  

联合体:继承 ctypes.Union,语法类似结构体

4. 特殊整型类型(别名类型)

ctypes 类型

C 类型

说明

c_int8

int8_t

8 位有符号整型(别名 c_byte)

c_int16

int16_t

16 位有符号整型(别名 c_short)

c_int32

int32_t

32 位有符号整型(别名 c_int)

c_int64

int64_t

64 位有符号整型(别名 c_longlong)

注意事项

1.平台差异:

  • c_long 和 c_int 的位宽可能因平台而异(例如 32 位和 64 位系统)
  • c_longdouble 在 sizeof(long double) == sizeof(double) 的平台上等价于 c_double

2. 内存管理:

  • 使用 c_char_p 接收 C 返回的字符串时,需确保 C 侧内存由 Python 显式释放(若动态分配)

3. 编码与类型转换:

  • 字符串需通过 encode("utf-8") 转为字节流后再传给 c_char_p
  • c_void_p 可接受整数表示的地址或 None

4.结构体对齐:

  • 可通过 _pack_ 属性调整结构体字段的内存对齐方式(如 _pack_ = 4 表示 4 字节对齐)

使用ctypes调用Go中的方法

Go编译.so文件指令

go build -buildmode=c-shared -o tt.so main.go

c_int类型

Go方法中接收一个c_int类型的数字,处理后,返回一个c_int类型的数字

Go代码

Note:Go语言中int类型跟操作系统有关,在32位系统上通常为32位,在64位系统上通常为64位,C.int转为Go中的int类型,直接使用强转就可以。前提要保证数据不能截断

package main

import "C"
import (
    "fmt"
)

//export CintDemo
func CintDemo(number C.int) C.int {
  go_number := int(number)
  fmt.Printf("c_int = %v\n", go_number)
  return number * 2
}

func main() {}

Python代码

from ctypes import CDLL, c_int

lib = CDLL("./tt.so")

lib.CintDemo.argtypes = [c_int]
lib.CintDemo.restype = c_int

res = lib.CintDemo(101)
print(f"response from lib: {res}")

输出结果

c_int = 101
response from lib: 202

c_char类型

Go方法中接收一个c_char类型,处理后,返回一个c_char类型

c中字符串以指针的类型传递,输入输出都采用指针类型

Go代码

package main

import "C"

//export CcharDemo
func CcharDemo(str *C.char) *C.char {
  goStr := C.GoString(str)
  newStr := goStr + " response from Go"
  cStr := C.CString(newStr)
  return cStr
}

func main() {}

Python代码

from ctypes import CDLL, c_char_p

lib = CDLL("./tt.so")

lib.CcharDemo.argtypes = [c_char_p]
lib.CcharDemo.restype = c_char_p

res = lib.CcharDemo("str from python".encode("utf8"))
print(f"response from lib: {res.decode('utf8')}")

输出结果

response from lib: str from python response from Go

Process finished with exit code 0

c_double类型

Go方法中接收一个c_double类型,处理后,返回一个c_double类型

Go代码

package main

import "C"
import "fmt"

//export CdoubleDemo
func CdoubleDemo(number C.double) C.double {
  goNumber := float64(number)
  fmt.Printf("goNumber: %v; type: %T\n", goNumber, goNumber)
  return number * 3
}

func main() {}

Python代码

from ctypes import CDLL, c_double

lib = CDLL("./tt.so")

lib.CdoubleDemo.argtypes = [c_double]
lib.CdoubleDemo.restype = c_double

res = lib.CdoubleDemo(66.66)
print(f"response from lib: {res}")

输出结果

goNumber: 66.66; type: float64
response from lib: 199.98

Process finished with exit code 0

c_bool类型

Go方法中接收一个c_bool类型,处理后,返回一个c_bool类型

Go代码

note: 记得引入stdbool.h头文件,在Go中想要直接使用C_bool类型,就要引入该文件,引入后,可以使用bool作为_Bool的别名,使代码更易读懂和与其他语言中布尔值类型标识相统一,同时该头文件还定义了ture跟false两个宏,分别对应_Bool类型的1和0

_Bool类型是C语言中表示布尔值的基本类型,在内存中通常占用1个字节。它只有两个取值,0表示假,非0表示真

C.bool值的写法

package main

/*
# include 
*/
import "C"
import "fmt"

//export CboolDemo
func CboolDemo(flag C.bool) C.bool {
	fmt.Println("flag from python =", flag)
	if flag {
		fmt.Println("bool value is true")
		flag = false
	} else {
		fmt.Println("bool value is false")
		flag = true
	}
	// goFlag := bool(flag)
	return flag
}

func main() {

C._Bool值的写法

package main

import "C"
import (
	"fmt"
	"unsafe"
)

//export CboolDemo
func CboolDemo(flag C._Bool) C._Bool {
	fmt.Printf("flag from python: %v, size: %v\n", flag, unsafe.Sizeof(flag))
	if flag {
		fmt.Println("bool value is true")
		flag = false
	} else {
		fmt.Println("bool value is false")
		flag = true
	}
	return flag
}

func main() {}

Python代码

from ctypes import CDLL, c_bool

lib = CDLL("./tt.so")

lib.CboolDemo.argtypes = [c_bool]
lib.CboolDemo.restype = c_bool

res = lib.CboolDemo(False)
print(f"response from lib: {res}")

输出结果

运行C._Bool类型的代码

flag from python: false, size: 1
bool value is false
response from lib: True

Process finished with exit code 0

c_void_p类型

Go方法中接收一个*C.char类型,处理后,返回一个结构体类型的指针

note:在使用指针类型时,由于Go语言有自己的垃圾回收机制,还有就是Go语言可能会将数据在内存中的保存位置进行移动,这样指针就会跟着移动,所以在操作指针类型时,最好使用C库中的malloc分配内存,然后再手动释放内存,记得使用完成后,一定要及时的释放Go中开辟的内存,防止内存泄漏。

Note: 使用C.malloc() 时,不用先定义好结构体,因为如果结构体变量通过声明定义,其内存位于栈区或者静态存储区,声明周期由作用域或者程序运行周期决定,再次执行malloc时,只是在堆区重新开辟了一块空间,所以在处理结构体时,只需要先malloc一个结构体,这样会在堆区为结构体分配好内存,分配好内存以后,通过强制转换,为结构体赋值,结构体处理完成以后,再强转为对应的结构体返回即可

不适用malloc(), Python调用时,会产生如下错误:

panic: runtime error: cgo result is unpinned Go pointer or points to unpinned Go pointer

Go代码

1. 使用头文件,并在Go中提前定义好C语言格式中结构体

package main

/*
#include 
#include 
typedef struct {
	char* result;
} StringResult;

StringResult* CpointerDemo(char* input);
*/
import "C"
import (
	"fmt"
	"unsafe"
)

//export CpointerDemo
func CpointerDemo(msg *C.char) *C.StringResult {
	goStr := C.GoString(msg)
	goStr = goStr + " content from Go code"
	cStr := C.CString(goStr)
	cPtr := C.malloc(C.size_t(unsafe.Sizeof(C.StringResult{})))
	(*C.StringResult)(cPtr).result = cStr
	return (*C.StringResult)(cPtr)
}

//export FreeeResource
func FreeeResource(handle *C.StringResult) {
	gPtr := unsafe.Pointer(handle)
	// 如果需要,可以使用如下办法将结构体中的数据再次使用
	msgPtr := &(*C.StringResult)(gPtr).result
	fmt.Println("msgPtr:", C.GoString(*msgPtr))
	// 释放内存
	C.free(gPtr)
	gPtr = nil
	fmt.Println("free memory successfully")
}

func main() {}

2. 使用C语言中的void*

void*被称为无类型指针,它不指向任何特定的数类型的数据,只是一个通用的指针,用于存储内存地址,它可以指向任何类型的数据对象,但在使用时通常需要进行类型转换,以便让编译器知道实际指向的数据类型。

主要用途:

通用数据存储和传递:在函数间传递数据时,如果不确定具体的数据类型,可以使用void*。如malloc函数用于动态分配内存,返回值为void*,可根据需要转换为其他类型的指针,int *ptr = (int*)malloc(sizeof(int));。 实现泛型编程:C语言没有像C++那样的模板机制,但可以使用void*实现类似泛型的功能,如qsort函数可以对不同类型的数据排序:void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));,通过void*指针能处理各种数据类型的数组。 作为函数指针类型:函数指针可以指向不同参数和返回值类型的函数,有时会用void*来表示函数指针类型,尤其是在需要将函数指针作为参数传递给其他函数,且不关心函数具体参数和返回值类型时。

使用注意事项:

类型转换:使用void*指针访问或操作数据时,必须进行显示类型转换,以确保编译器按正确数据类型处理。 指针运算:void*指针不能直接进行指针运算,因为编译器不知道其指向数据类型大小,无法确定指针移动步长,需转换为具体类型指针后再进行运算。

package main

/*
# include 
typedef void* PointerDemo;
*/
import "C"
import (
	"fmt"
	"unsafe"
)

type SturctDemo struct {
	Msg *string
}

//export CpointerDemo
func CpointerDemo(msg *C.char) *C.PointerDemo {
	goStr := C.GoString(msg)
	goStr = goStr + " content from Go code"
	// 直接在堆内存为结构体分配空间
	ptr := (*SturctDemo)(C.malloc(C.size_t(unsafe.Sizeof(SturctDemo{}))))
	if ptr == nil {
		panic("malloc failed")
	}
	ptr.Msg = &goStr
	return (*C.PointerDemo)(unsafe.Pointer(ptr))
}

//export FreeeResource
func FreeeResource(handle *C.PointerDemo) {
	gPtr := unsafe.Pointer(handle)
	// 如果需要,可以使用如下办法将结构体中的数据再次使用
	msgPtr := (*SturctDemo)(gPtr).Msg
	fmt.Println("msgPtr:", *msgPtr)
	// 释放内存
	C.free(gPtr)
	gPtr = nil
	fmt.Println("free memory successfully")
}

func main() {}

Python代码

from ctypes import CDLL, c_char_p, c_void_p

lib = CDLL("./tt.so")

lib.CpointerDemo.argtypes = [c_char_p]
lib.CpointerDemo.restype = c_void_p

lib.FreeeResource.argtypes = [c_void_p]

res = lib.CpointerDemo("message from python".encode("utf8"))
print(f"response from lib: {res}")
lib.FreeeResource(res)

输出结果

response from lib: 2322181954544
msgPtr: message from python content from Go code
free memory successfully

Process finished with exit code 0

int 数组类型

Go方法中接收一个int类型数组,处理后,返回一个int类型数组

Go代码

package main

/*
#include 
typedef struct {
	int* data;
	int len;
} IntArray;
*/
import "C"
import (
	"fmt"
	"unsafe"
)

//export CintArrayDemo
func CintArrayDemo(cArray *C.int, cLength C.int) *C.IntArray {
	// 转换C数组为Go切片
	goSlice := (*[1 << 30]C.int)(unsafe.Pointer(cArray))[:cLength:cLength]

	// 处理元素,将每个元素*2
	result := make([]int, cLength)
	for i := 0; i < int(cLength); i++ {
		result[i] = int(goSlice[i]) * 2
	}
	wrapper := (*C.IntArray)(C.malloc(C.size_t(unsafe.Sizeof(C.IntArray{}))))
	wrapper.data = (*C.int)(C.malloc(C.size_t(cLength * C.sizeof_int)))
	wrapper.len = cLength

	for i := 0; i < int(cLength); i++ {
		(*[1 << 30]C.int)(unsafe.Pointer(wrapper.data))[i] = C.int(result[i])
	}
	return wrapper
}

//export FreeeResource
func FreeeResource(handle *C.IntArray) {
	C.free(unsafe.Pointer(handle.data))
	C.free(unsafe.Pointer(handle))
	fmt.Println("free memory successfully")
}

func main() {}

Python代码

arr = (c_int * len(input_data))(*input_data)

这行代码就使用到了“数组与复合类型”章节提到的语法,本质是C语言数组的实例化对象,其行为类似于C语言中的数组名(隐式指向首元素地址的指针),在实际跨语言交互中,arr 会被自动转换为指向首元素的指针(int*),满足与C函数交互的需求。

from ctypes import CDLL, c_int, POINTER, Structure

class IntArray(Structure):
    _fields_ = [
        ("data", POINTER(c_int)),
        ("len", c_int)
    ]



lib = CDLL("./tt.so")

lib.CintArrayDemo.argtypes = [POINTER(c_int), c_int]
lib.CintArrayDemo.restype = POINTER(IntArray)

lib.FreeeResource.argtypes = [POINTER(IntArray)]

input_data = [10, 20, 30, 40]
arr = (c_int * len(input_data))(*input_data)
# print(arr)

ptr = lib.CintArrayDemo(arr, len(input_data))
result = [ptr.contents.data[i] for i in range(ptr.contents.len)]
print(f"response from lib: {result}")
lib.FreeeResource(ptr)

运行结果

response from lib: [20, 40, 60, 80]
free memory successfully

字符串数组类型

Go方法中接收一个字符串类型(char*)数组指针(**char),处理后,返回一个字符串(*char)类型数组指针(**char)

Go代码

package main

/*
#include 
*/
import "C"
import (
	"fmt"
	"strings"
	"unsafe"
)

//export CcharArrayDemo
func CcharArrayDemo(input **C.char, length C.int) **C.char {
	// 将C字符串数组转换为Go的[]string
	cStrings := (*[1 << 30]*C.char)(unsafe.Pointer(input))[:length:length]

	goStrings := make([]string, length)
	for i := 0; i < int(length); i++ {
		goStrings[i] = C.GoString(cStrings[i])
	}

	// 处理字符串数组,将所有的字符串转为大写
	for i, v := range goStrings {
		goStrings[i] = strings.ToUpper(v)
	}

	// 将Go字符串数组转回C字符串数组
	cArray := C.malloc(C.size_t(length) * C.size_t(unsafe.Sizeof(uintptr(0))))
	cStringsOut := (*[1 << 30]*C.char)(cArray)
	for i, v := range goStrings {
		cStringsOut[i] = C.CString(v)
	}
	return (**C.char)(cArray)
}

//export FreeeResource
func FreeeResource(cArray **C.char, length C.int) {
	cStrings := (*[1 << 30]*C.char)(unsafe.Pointer(cArray))[:length:length]
	for i := 0; i < int(length); i++ {
		C.free(unsafe.Pointer(cStrings[i]))
	}
	C.free(unsafe.Pointer(cArray))
	fmt.Println("free memory successfully")
}

func main() {}

Python代码

from ctypes import CDLL, c_int, POINTER, c_char_p, string_at


lib = CDLL("./tt.so")

lib.CcharArrayDemo.argtypes = [POINTER(c_char_p), c_int]
lib.CcharArrayDemo.restype = POINTER(c_char_p)

lib.FreeeResource.argtypes = [POINTER(c_char_p), c_int]

input_strings = ["hello", "world"]
input_length = len(input_strings)
c_input = (c_char_p * input_length)()
for i, v in enumerate(input_strings):
    c_input[i] = v.encode("utf8")

ptr = lib.CcharArrayDemo(c_input, input_length)

result = []
for x in range(input_length):
    result.append(string_at(ptr[x]).decode("utf8"))

print(f"response from lib: {result}")
lib.FreeeResource(ptr, input_length)

运行结果

response from lib: ['HELLO', 'WORLD']
free memory successfully

相关推荐

Python 最常用的语句、函数有哪些?

1.#coding=utf-8①代码中有中文字符,最好在代码前面加#coding=utf-8②pycharm不加可能不会报错,但是代码最终是会放到服务器上,放到服务器上的时候运行可能会报错。③...

PyYAML 实用的使用技巧

作者:Reorx’sForge中文版:https://reorx.com/blog/python-yaml-tips-zh英文版:Tipsthatmaysaveyoufromthehe...

学习编程第127天 python中字符串与数值中的巧妙应用

今天学习的刘金玉老师零基础Python教程第10期,主要内容是python中字符串与数值中的巧妙应用。一、新建一个工程如图,新建一个工程。二、字符串与数值的区别变量只有为数值的时候,才能进行数学运算。...

Python 必学!12 个 “开挂级” 内置函数深度解析(小白也能秒懂)

干货来了以下是Python中12个强大内置函数的深度解析,涵盖数据处理、代码优化和高级场景,助你写出更简洁高效的代码:一、数据处理三剑客1.map(function,iterable)作用:...

Python浮点数保留两位小数的方法

技术背景在Python编程中,经常会遇到需要将浮点数保留特定小数位数的情况,比如在处理货币、统计数据等场景。然而,由于浮点数在计算机中采用二进制表示,存在精度问题,导致直接使用round函数有时无法得...

DAY4-step5 Python示例说明 round()函数

Round()Round()是python提供的内置函数。它将返回一个浮点数,该浮点数将四舍五入到指定的精度。如果未指定要舍入的小数位,则将其视为0,并将舍入到最接近的整数。语法:round(flo...

第五个测试版本了,iOS 9 又有了什么变化?

今天的早些时候苹果发布了iOS9beta5,除去修复BUG和提升系统的稳定性外,苹果还带来了一些新功能。本次更新包括了对Carplay,WiFi以及Siri等功能的优化,Mac...

如何在 Python 中随机排列列表元素

在本教程中,我们将学习在Python中如何打乱列表元素顺序,随机排列列表元素。如何随机排列列表是一项非常有用的技能。它在开发需要选择随机结果游戏中非常有用。它还适用于数据相关的工作中,可能需要提取...

Python获取随机数方法汇总

1.random.random()作用:随机生成一个[0,1)之间的浮点数importrandomprint(f'随机生成一个[0,1)之间的浮点数={random.random()}&...

Python程序开发之简单小程序实例(11)小游戏-跳动的小球

Python程序开发之简单小程序实例(11)小游戏-跳动的小球一、项目功能用户控制挡板来阻挡跳动的小球。二、项目分析根据项目功能自定义两个类,一个用于控制小球在窗体中的运动,一个用于接收用户按下左右键...

Keras人工智能神经网络 Regressor 回归 神经网络搭建

前期分享了使用tensorflow来进行神经网络的回归,tensorflow构建神经网络本期我们来使用Keras来搭建一个简单的神经网络Keras神经网络可以用来模拟回归问题(regression)...

我让DeepSeek写程序,有懂的看看写的对不对?

他写的时候就像教学生解方程一样,解释一段写一段,因为中间太长,我就截了最后的结果,应该是手机版本复制字数有限,可能也没复制全,有没有懂的看看写的对不对?下面是他写的最后结果。importrandom...

Picker Wheel 线上随机抽签轮盘

#头条创作挑战赛#办公室经常会碰到「中午要吃什么?」、「要订哪家饮料店?」或「谁要去帮大家跑腿?」等各种情境,为了公平起见,我们可以使用随机方式进行抽签,这样一来就能确保公平公正性,其他人也就不...

思维链COT原理探究

TEXTANDPATTERNS:FOREFFECTIVECHAINOFTHOUGHTITTAKESTWOTOTANGO测试模型:PaLM-62B,GPT3,CODEXCOT元素...

永别了iPod!系列产品回顾,你用过几款?

中关村在线消息:就在本周,苹果官方宣布iPod系列将不再生产新品,现货售完即止。作为一个偏向音乐播放的系列,iPod系列想必陪伴了很多朋友的学生年代。近日有外媒总结了iPod系列的全部产品,来看看你用...