Linux下 Python通过ctypes调用cAPI (一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zoe9698/article/details/79670591
c_long和c_int是一种类型。
cdll加载使用标准cdecl调用约定导出函数的库
windll使用stdcall调用约定调用函数
oledll也使用stdcall调用约定

在linux下通过两种方法加载动态链接库
1.cdll.LoadLibrary("libc.so.6")
2.CDLL("libc.so.6")
(结果相同)

如何告诉Python一个外来函数的形参类型和返回的值的类型呢

答:给函数的两个属性restype和argtypes赋值。
restype-------返回类型
argtypes------参数类型
例如某函数的返回类型是float,对应到Python里就是c_float。
dll.addf.restype = c_float

如果函数的返回值是void那么可以赋值为None。另外,可以使用python 内置类型“描述”库函数的返回类型,但是,不可以用python内置类型来描述库函数的参数类型。


由于函数的参数不是固定的数量,所以需要使用列表或者是元祖来说明:
dll.addf.argtypes = (c_float,c_float)
or
dll.addf.argtypes = [c_float,c_float]

创建一个ctypes的类型(c_int、c_float、c_char...)并给他赋值
i = c_int(45)
i.value
or
i.value = 56
i.value

结构体跳过

指针
创建一个ctypes的指针:
1.byref(x[,offset]):返回x的地址,x必须为ctypes类型的一个实例。相当于c的&x。offset表示偏移量。(通过引用传递参数)
2.pointer(x):创建并返回一个指向x的指针实例,x是一个实例对象。(构造了一个真正的指针对象,因此这种方法做了很多工作,会更慢相对byref)
3.POINTER(type):返回一个类型,这个类型是指向type类型的指针类型,type是ctypes的一个类型。
eg:
>>> a = c_int(66)         # 创建一个 c_int 实例
>>> b = pointer(a)        # 创建指针
>>> c = POINTER(c_int)(a) # 创建指针
>>> b
<__main__.LP_c_long object at 0x00E12AD0>
>>> c
<__main__.LP_c_long object at 0x00E12B20>
>>> b.contents            # 输出 a 的值
c_long(66)
>>> c.contents            # 输出 a 的值
c_long(66)

令POINTER(c_int)为pointer

pointer创建的指针貌似不能修改ctypes类型

print_point函数:
>>> dll.print_point.argtypes = (POINTER(Point),)   # 指明函数的参数类型
>>> dll.print_point.restype = None                 # 指明函数的返回类型
>>>
>>> p = Point(32.4, -92.1)      # 实例化一个 Point
>>> dll.print_point(byref(p))   # 调用函数
position x 32.400002 y -92.099998>>>

当然你非要用慢一点的 pointer 也行:

>>> dll.print_point(pointer(p))  # 调用函数
position x 32.400002 y -92.099998

关于byref()-即用引用传递参数:
一般适用于需要C函数修改传入的参数,或者参数过大不适合传值,这时就要用到按引用传递。
eg:
from ctypes import *
'''
windows下
libc = cdll.LoadLibrary('msvcrt.dll')
'''
#linux下
libc = cdll.LoadLibrary('libc.so.6')
    
i = c_int()
f = c_float()
s = create_string_buffer(b'\000' * 32)
print 'i.val =', i.value
print 'f.val =', f.value
print 'repr(s.value) =', repr(s.value)
libc.sscanf(b'1 3.14 Hello', b'%d %f %s', byref(i), byref(f), s)
#3表示正确返回,0 表示不正确返回
print 'after, i.val =', i.value
print 'after, f.val =', f.value
print 'after, repr(s.value) =', repr(s.value)
输出:
i.val = 0
f.val = 0.0
repr(s.value) = ''
after, i.val = 1
after, f.val = 3.1400001049
after, repr(s.value) = 'Hello'

数组
数组是序列,包含固定数量的相同类型的实例。
推荐的创建数组类型的方法是将数据类型乘以正整数:
TenPointsArrayType  =  POINT  *  10
官方文档给出的例子是eg:
>>> from  ctypes  import  *
>>> class  POINT (Structure ):
...     _fields_  =  (“x” , c_int ), (“y” , c_int )
...
>>> class  MyStruct (Structure ):
.. 。    _fields_  =  [( “ 一” , c_int的),
...                 (“b” , c_float ),
...                 (“point_array” , POINT  * 4 )]
>>>
>>> print (len (MyStruct ()。point_array ))
4

更好的例子eg:
>>> class POINT(Structure):
#括号里第一个参数是形参的名字
...     _fields_=("x",c_int),("y",c_int)
...
>>> class MyStruct(Structure):
...     _fields_=[("a",c_int),("b",c_float),("point_array",POINT*4)]
...
>>> print(len(MyStruct().point_array))
4
>>> TenIntArrayType = c_int * 10
#如果不赋值,默认是0
>>> ta = TenIntArrayType(1,2,3,4,5,6,7,8,9,10)
>>> for item in ta:
...     print(item)
...
1
2
3
4
5
6
7
8
9
10
>>> ms = MyStruct(4,5,((1,1),(2,2),(3,3),(4,4)))
>>> for item in ms.point_array:
...     print('(item.x,item.y) = (%d,%d)' % (item.x,item.y))
...
(item.x,item.y) = (1,1)
(item.x,item.y) = (2,2)
(item.x,item.y) = (3,3)
(item.x,item.y) = (4,4)
>>>

pointer()函数指针:
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>> pi.contents
c_long(42)
注意ctypes都没有原始对象返回,也就是说每次调用pi属性时重新创建一个新的对象,那么就是:
>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
在幕后,pointer()不仅创建指针实例,它必须首先创建指针类型。这是通过POINTER()接受任何ctypes类型的函数完成的,并返回一个新类型。
调用没有参数的指针类型将创建一个NULL指针。 NULL指针有一个False布尔值:
>>> null_ptr  =  POINTER (c_int )()
>>> print (bool (null_ptr ))
False
>>>


参考:点击打开链接

点击打开链接

这个↓是各种类型参数从python向c传递的教程!!!

点击打开链接

展开阅读全文

没有更多推荐了,返回首页