0
# Memory Management
1
2
C memory allocation, deallocation, garbage collection, and address operations. CFFI provides automatic memory management with options for custom allocators and manual control.
3
4
## Capabilities
5
6
### Memory Allocation
7
8
Allocates C data structures with automatic garbage collection and optional initialization.
9
10
```python { .api }
11
def new(self, cdecl, init=None):
12
"""
13
Allocate C data according to the specified type.
14
15
Parameters:
16
- cdecl (str): C type declaration (pointer or array)
17
- init: Optional initializer value
18
19
Returns:
20
CData object with automatic memory management
21
"""
22
```
23
24
**Usage Examples:**
25
26
```python
27
# Allocate single values
28
p_int = ffi.new("int *")
29
p_int[0] = 42
30
31
# Allocate with initialization
32
p_initialized = ffi.new("int *", 100)
33
34
# Allocate arrays
35
arr = ffi.new("int[10]") # Fixed size array
36
dyn_arr = ffi.new("int[]", 5) # Dynamic array with 5 elements
37
38
# Initialize arrays
39
data = ffi.new("int[]", [1, 2, 3, 4, 5])
40
41
# Allocate structures
42
ffi.cdef("struct point { int x, y; };")
43
point = ffi.new("struct point *")
44
point.x = 10
45
point.y = 20
46
47
# Initialize structures
48
point_init = ffi.new("struct point *", {"x": 10, "y": 20})
49
```
50
51
### Custom Allocators
52
53
Creates custom memory allocators with user-defined allocation and deallocation functions.
54
55
```python { .api }
56
def new_allocator(self, alloc=None, free=None, should_clear_after_alloc=True):
57
"""
58
Create custom allocator function.
59
60
Parameters:
61
- alloc: Custom allocation function (takes size, returns pointer)
62
- free: Custom deallocation function (takes pointer)
63
- should_clear_after_alloc (bool): Whether to zero-initialize memory
64
65
Returns:
66
Allocator function compatible with new() interface
67
"""
68
```
69
70
**Usage Example:**
71
72
```python
73
# Create custom allocator
74
def my_alloc(size):
75
print(f"Allocating {size} bytes")
76
return ffi.cast("void *", ffi.new("char[]", size))
77
78
def my_free(ptr):
79
print("Freeing memory")
80
# Memory automatically freed by CFFI
81
82
allocator = ffi.new_allocator(my_alloc, my_free)
83
84
# Use custom allocator
85
data = allocator("int[100]")
86
```
87
88
### Type Casting
89
90
Converts between different C types and Python objects.
91
92
```python { .api }
93
def cast(self, cdecl, source):
94
"""
95
Cast source to the specified C type.
96
97
Parameters:
98
- cdecl (str): Target C type declaration
99
- source: Source value (CData, int, pointer, etc.)
100
101
Returns:
102
CData object of the specified type
103
"""
104
```
105
106
**Usage Examples:**
107
108
```python
109
# Cast integers to pointers
110
ptr = ffi.cast("void *", 0x12345678)
111
112
# Cast between pointer types
113
int_ptr = ffi.new("int *", 42)
114
void_ptr = ffi.cast("void *", int_ptr)
115
back_to_int = ffi.cast("int *", void_ptr)
116
117
# Cast arrays to pointers
118
arr = ffi.new("int[5]", [1, 2, 3, 4, 5])
119
ptr = ffi.cast("int *", arr)
120
121
# Cast to different integer types
122
value = ffi.cast("unsigned char", 256) # Results in 0 (overflow)
123
```
124
125
### Address Operations
126
127
Gets the address of C data objects and structure fields.
128
129
```python { .api }
130
def addressof(self, cdata, *fields_or_indexes):
131
"""
132
Get address of C data or field within structure/array.
133
134
Parameters:
135
- cdata: C data object
136
- *fields_or_indexes: Field names or array indexes for nested access
137
138
Returns:
139
Pointer to the specified location
140
"""
141
```
142
143
**Usage Examples:**
144
145
```python
146
# Get address of simple data
147
x = ffi.new("int *", 42)
148
addr = ffi.addressof(x[0])
149
150
# Get address of structure fields
151
ffi.cdef("struct point { int x, y; };")
152
point = ffi.new("struct point *")
153
x_addr = ffi.addressof(point, "x")
154
y_addr = ffi.addressof(point, "y")
155
156
# Get address of array elements
157
arr = ffi.new("int[10]")
158
element_addr = ffi.addressof(arr, 5)
159
160
# Nested structure access
161
ffi.cdef("""
162
struct inner { int value; };
163
struct outer { struct inner data[5]; };
164
""")
165
outer = ffi.new("struct outer *")
166
inner_addr = ffi.addressof(outer, "data", 2, "value")
167
```
168
169
### Garbage Collection Control
170
171
Attaches custom destructors to C data objects for cleanup when garbage collected.
172
173
```python { .api }
174
def gc(self, cdata, destructor, size=0):
175
"""
176
Attach destructor to C data object.
177
178
Parameters:
179
- cdata: C data object
180
- destructor: Function to call on garbage collection
181
- size (int): Estimated size for GC scheduling
182
183
Returns:
184
New CData object with attached destructor
185
"""
186
```
187
188
**Usage Example:**
189
190
```python
191
def cleanup_func(ptr):
192
print("Cleaning up resource")
193
# Perform cleanup operations
194
195
# Allocate resource with cleanup
196
resource = ffi.new("void **")
197
managed_resource = ffi.gc(resource, cleanup_func)
198
```
199
200
## Memory Safety Patterns
201
202
### Automatic Memory Management
203
204
```python
205
def process_data():
206
# Memory automatically freed when function exits
207
buffer = ffi.new("char[1024]")
208
# Use buffer...
209
return ffi.string(buffer) # Safe: string is copied
210
```
211
212
### Long-lived References
213
214
```python
215
class DataProcessor:
216
def __init__(self):
217
# Keep reference to prevent garbage collection
218
self.buffer = ffi.new("char[4096]")
219
self.processed = ffi.gc(self.buffer, self._cleanup)
220
221
def _cleanup(self, ptr):
222
print("Buffer cleaned up")
223
```
224
225
### Working with C Libraries that Manage Memory
226
227
```python
228
ffi.cdef("""
229
void* create_object(int size);
230
void destroy_object(void* obj);
231
""")
232
233
lib = ffi.dlopen("mylib.so")
234
235
# Create object managed by C library
236
obj = lib.create_object(1024)
237
238
# Attach cleanup function
239
managed_obj = ffi.gc(obj, lib.destroy_object)
240
```
241
242
## Common Pitfalls and Solutions
243
244
### Dangling Pointers
245
246
```python
247
# WRONG: pointer becomes invalid
248
def get_pointer():
249
data = ffi.new("int *", 42)
250
return ffi.addressof(data[0]) # Danger: data may be freed
251
252
# CORRECT: return the data object itself
253
def get_data():
254
data = ffi.new("int *", 42)
255
return data # Safe: data stays alive
256
```
257
258
### Memory Sharing
259
260
```python
261
# Share memory between Python and C
262
source = bytearray(b"Hello, World!")
263
c_buffer = ffi.from_buffer("char[]", source)
264
265
# Modifications to c_buffer affect source
266
c_buffer[0] = ord('h')
267
print(source) # b"hello, World!"
268
```
269
270
### Array Bounds
271
272
```python
273
# CFFI doesn't check array bounds - be careful!
274
arr = ffi.new("int[5]")
275
# arr[10] = 42 # DANGER: out of bounds access
276
277
# Safe pattern: check bounds manually
278
def safe_set(arr, index, value, size):
279
if 0 <= index < size:
280
arr[index] = value
281
else:
282
raise IndexError("Array index out of range")
283
```