Lab 05: C Extension via ctypes

Time: 60 minutes | Level: Architect | Docker: docker run -it --rm python:3.11-slim bash

Overview

ctypes provides a Foreign Function Interface (FFI) to call C libraries without writing C code. This lab covers calling libc functions, defining C structures, creating callbacks, and understanding the architectural trade-offs vs. cffi.

Step 1: Loading C Libraries

import ctypes
import sys

# Load the C standard library
if sys.platform == 'linux':
    libc = ctypes.CDLL("libc.so.6")
elif sys.platform == 'darwin':
    libc = ctypes.CDLL("libc.dylib")
else:
    libc = ctypes.cdll.msvcrt  # Windows

# Call simple functions
libc.puts(b"Hello from libc.puts!")

# strlen
libc.strlen.restype = ctypes.c_size_t
libc.strlen.argtypes = [ctypes.c_char_p]
length = libc.strlen(b"hello world")
print(f"strlen('hello world') = {length}")

# abs
libc.abs.restype = ctypes.c_int
libc.abs.argtypes = [ctypes.c_int]
print(f"abs(-42) = {libc.abs(-42)}")

💡 Always set .restype and .argtypes on ctypes functions! Without them, ctypes assumes c_int return and no type checking — a common source of segfaults.

Step 2: Basic Types

Step 3: Calling qsort with Callback

📸 Verified Output:

Step 4: ctypes.Structure — Mapping C Structs

Step 5: ctypes.Union

Step 6: ctypes.cast and Pointer Arithmetic

Step 7: malloc / free via ctypes

💡 Always free memory allocated with malloc. Python's GC doesn't know about raw C allocations.

Step 8: Capstone — Shared Library Wrapper

Build a complete, safe wrapper around libc math functions:

📸 Verified Output (qsort):

Summary

Concept
API
Use Case

Load shared lib

ctypes.CDLL

Call C libraries

Type safety

.restype, .argtypes

Prevent segfaults

Callbacks

CFUNCTYPE

Pass Python funcs to C

C structs

ctypes.Structure._fields_

Binary data layout

C unions

ctypes.Union

Variant data types

Pointer cast

ctypes.cast

Reinterpret memory

Raw allocation

malloc/free via ctypes

Unmanaged memory

Wrapper pattern

Class encapsulation

Safe C library binding

Last updated