### Python ctypes 模块知识点详解
#### 一、加载动态链接库
`ctypes`模块在Python中主要用于加载和调用C语言编写的共享库(动态链接库)。无论是Windows下的`.dll`文件还是Linux下的`.so`文件,都可以通过`ctypes`进行加载。
**1.1 加载方式**
- **在Windows上**:
- 使用`windll`加载使用`stdcall`调用约定的库;
- 使用`cdll`加载使用`cdecl`调用约定的库。
- `windll`和`cdll`都会自动添加`.dll`后缀。
```python
from ctypes import *
# 加载Windows内核32位动态链接库
kernel32 = windll.kernel32
```
- **在Linux上**:
- 必须显式地提供动态链接库的完整路径及文件名,包括扩展名。
```python
libc = CDLL('libc.so.6')
```
**1.2 示例**
- **Windows示例**:
- 加载`kernel32.dll`:
```python
from ctypes import *
print(windll.kernel32) # 输出 <WinDLL 'kernel32', handle at ...>
```
- 加载`msvcrt.dll`:
```python
libc = cdll.msvcrt
print(libc) # 输出 <CDLL 'msvcrt', handle at ...>
```
- **Linux示例**:
- 加载`libc.so.6`:
```python
from ctypes import *
libc = CDLL('libc.so.6')
print(libc) # 输出 <CDLL 'libc.so.6', handle at ...>
```
#### 二、从已加载的dll中引用函数
一旦动态链接库被加载,可以通过类似访问对象属性的方式获取到库中的函数。
**2.1 函数访问**
```python
from ctypes import *
libc = cdll.msvcrt # 假设在Windows下加载msvcrt.dll
printf = libc.printf # 获取库中的printf函数
```
**2.2 注意事项**
- **函数未找到**:
- 如果尝试访问不存在的函数会抛出`AttributeError`异常。
- 在Windows环境下,某些库(如`kernel32.dll`)可能会提供不同字符集版本的函数,如ANSI版(以`A`结尾)和UNICODE版(以`W`结尾)。
#### 三、调用函数
加载完库并获取到所需函数后,可以直接调用这些函数。
**3.1 函数调用**
```python
from ctypes import *
# 假设在Windows下
kernel32 = windll.kernel32
GetModuleHandleA = kernel32.GetModuleHandleA # 获取GetModuleHandleA函数
# 调用GetModuleHandleA函数
handle = GetModuleHandleA(b"kernel32")
print(handle)
```
**3.2 参数与返回值**
- **参数类型**:在调用函数之前,需要了解函数原型以确定参数类型和个数。
- **返回值**:函数的返回值通常也需要转换成Python可以理解的数据类型。
#### 四、基本的数据类型
`ctypes`提供了与C语言对应的数据类型,例如:
- `c_char`:单个字符
- `c_int`:整型
- `c_float`:浮点型
- `c_double`:双精度浮点型
- `c_void_p`:空指针类型
**4.1 示例**
```python
from ctypes import *
# 定义C语言中的int类型
my_int = c_int(10)
print(my_int.value) # 输出 10
```
#### 五、结构和联合
`ctypes`还支持定义结构体和联合体。
**5.1 结构体**
```python
class Point(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
p = Point(10, 20)
print(p.x, p.y) # 输出 10 20
```
**5.2 联合体**
```python
class Data(Union):
_fields_ = [("i", c_int), ("f", c_float), ("d", c_double)]
data = Data()
data.i = 10
print(data.f) # 输出 10.0
```
**5.3 对齐方式和字节顺序**
- **对齐方式**:`ctypes`默认使用平台的自然对齐方式。
- **字节顺序**:`ctypes`会根据平台自动处理大端和小端问题。
#### 六、数组与指针
- **数组**:使用类型加方括号表示数组。
- **指针**:使用`POINTER`函数创建指针类型。
**6.1 数组**
```python
arr = (c_int * 5)(1, 2, 3, 4, 5)
print(arr[0]) # 输出 1
```
**6.2 指针**
```python
from ctypes import *
arr = (c_int * 5)(1, 2, 3, 4, 5)
ptr = cast(arr, POINTER(c_int))
print(ptr[2]) # 输出 3
```
#### 七、回调函数
在C语言中,函数可以作为参数传递给其他函数。`ctypes`允许定义和使用回调函数。
**7.1 定义回调函数**
```python
from ctypes import CFUNCTYPE, c_int
# 定义回调函数类型
CALLBACK_FUNC = CFUNCTYPE(c_int, c_int)
# 实现回调函数
def my_callback(n):
return n * n
# 创建回调函数对象
callback_func = CALLBACK_FUNC(my_callback)
```
**7.2 使用回调函数**
```python
from ctypes import *
# 定义一个接受回调函数作为参数的C函数
def some_function(callback):
return callback(10)
result = some_function(callback_func)
print(result) # 输出 100
```
#### 八、其他概念
- **类型转换**:使用`cast`函数进行类型转换。
- **未完成的类型**:`ctypes`允许定义尚未完全定义的类型,例如指向结构体的指针,然后再定义结构体本身。
- **可变长度的数据类型**:支持创建可变长度的数组等。
- **Bugs和未实现的功能**:文档中提到的一些尚未解决的问题和未完成的功能。
以上就是从`ctypes`模块文档中提取和总结的关键知识点。通过这些知识点的学习,可以更好地掌握如何在Python中利用`ctypes`来调用C语言编写的库和函数。