Skip to content

How to Call a C Function in Python

Learn how to call C functions from Python using ctypes for performance-critical tasks, library reuse, or system-level API integration with step-by-step examples.

There are situations where you need to leverage C code from Python, whether for performance-critical computations, reusing an existing C library, or interfacing with system-level APIs. Python's built-in ctypes module makes this possible by providing a foreign function interface (FFI) that lets you load shared libraries and call C functions directly from Python.

This guide walks through the entire process: writing C code, compiling it into a shared library, and calling it from Python.

Why Call C from Python?

  • Performance: C is significantly faster than Python for CPU-intensive operations.
  • Reuse existing libraries: leverage well-tested C libraries without rewriting them.
  • Hardware/system access: interact with low-level system APIs written in C.
  • Legacy code: integrate existing C codebases into Python applications.

Step 1: Write the C Function

Create a C source file with the function(s) you want to call from Python.

mathutils.c:


// Check if a number is a power of 2
int is_power_of_two(int num) {
    if (num <= 0)
        return 0;
    return (num & (num - 1)) == 0 ? 1 : 0;
}

// Calculate factorial
long factorial(int n) {
    if (n <= 1)
        return 1;
    long result = 1;
    for (int i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

// Add two doubles
double add_doubles(double a, double b) {
    return a + b;
}

Step 2: Compile into a Shared Library

Compile the C file into a shared library (.so on Linux/macOS, .dll on Windows).

Linux

gcc -fPIC -shared -o libmathutils.so mathutils.c -lm

macOS

gcc -fPIC -shared -o libmathutils.dylib mathutils.c -lm

Windows (using MinGW)

gcc -shared -o mathutils.dll mathutils.c
Flag Purpose
-fPIC Generate position-independent code (required for shared libraries)
-shared Create a shared library instead of an executable
-o Specify the output file name
-lm Link the math library (if using math.h)

Step 3: Call from Python Using ctypes

The ctypes module is part of Python's standard library: no installation required.

import ctypes

lib = ctypes.CDLL("./libmathutils.so")  # Use full path if needed

lib.is_power_of_two.argtypes = [ctypes.c_int]
lib.is_power_of_two.restype = ctypes.c_int

print(f"Is 16 a power of 2? {lib.is_power_of_two(16)}")  # 1 (True)
print(f"Is 15 a power of 2? {lib.is_power_of_two(15)}")  # 0 (False)

lib.factorial.argtypes = [ctypes.c_int]
lib.factorial.restype = ctypes.c_long

print(f"Factorial of 10: {lib.factorial(10)}")  # 3628800

lib.add_doubles.argtypes = [ctypes.c_double, ctypes.c_double]
lib.add_doubles.restype = ctypes.c_double

print(f"3.14 + 2.86 = {lib.add_doubles(3.14, 2.86)}")  # 6.0

Output:

Is 16 a power of 2? 1
Is 15 a power of 2? 0
Factorial of 10: 3628800
3.14 + 2.86 = 6.0

Understanding argtypes and restype

Setting argtypes and restype is essential for correct behavior. Without them, ctypes assumes all arguments and return values are C int types, which causes incorrect results for floats, doubles, pointers, and other types.

lib.add_doubles.argtypes = [ctypes.c_double, ctypes.c_double]

lib.add_doubles.restype = ctypes.c_double

Common ctypes Type Mappings

C Type ctypes Type Python Type
int ctypes.c_int int
long ctypes.c_long int
float ctypes.c_float float
double ctypes.c_double float
char ctypes.c_char bytes
char* ctypes.c_char_p bytes
void* ctypes.c_void_p int or None
bool ctypes.c_bool bool

Working with Strings

Passing strings between Python and C requires using c_char_p for C-style strings:

strutils.c:


int string_length(const char* str) {
    return strlen(str);
}

Compile:

gcc -fPIC -shared -o libstrutils.so strutils.c

Python:

import ctypes

lib = ctypes.CDLL("./libstrutils.so")

lib.string_length.argtypes = [ctypes.c_char_p]
lib.string_length.restype = ctypes.c_int

result = lib.string_length(b"Hello, World!")
print(f"String length: {result}")

Output:

String length: 13

Working with Arrays

Pass Python arrays to C using ctypes array types:

arrayutils.c:

int sum_array(int* arr, int length) {
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += arr[i];
    }
    return sum;
}

Python:

import ctypes

lib = ctypes.CDLL("./libarrayutils.so")

lib.sum_array.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_int]
lib.sum_array.restype = ctypes.c_int

data = [10, 20, 30, 40, 50]
arr_type = ctypes.c_int * len(data)
c_array = arr_type(*data)

result = lib.sum_array(c_array, len(data))
print(f"Sum: {result}")

Output:

Sum: 150

Using cffi as an Alternative

The cffi library provides a more Pythonic and easier-to-use interface for calling C code:

pip install cffi
from cffi import FFI

ffi = FFI()

ffi.cdef("int is_power_of_two(int num);")

lib = ffi.dlopen("./libmathutils.so")

print(lib.is_power_of_two(16))  # 1
print(lib.is_power_of_two(15))  # 0

Complete Working Example

Here's a self-contained example you can copy and run:

demo.c:

int multiply(int a, int b) {
    return a * b;
}

double circle_area(double radius) {
    return 3.14159265358979 * radius * radius;
}

Compile:

gcc -fPIC -shared -o libdemo.so demo.c

demo.py:

import ctypes

lib = ctypes.CDLL("./libdemo.so")

lib.multiply.argtypes = [ctypes.c_int, ctypes.c_int]
lib.multiply.restype = ctypes.c_int

lib.circle_area.argtypes = [ctypes.c_double]
lib.circle_area.restype = ctypes.c_double

print(f"7 × 8 = {lib.multiply(7, 8)}")
print(f"Area of circle (r=5): {lib.circle_area(5.0):.4f}")

Output:

7 × 8 = 56
Area of circle (r=5): 78.5398

Conclusion

Calling C functions from Python using ctypes is a straightforward process: write the C code, compile it into a shared library, and load it in Python with proper type definitions.

  • Always set argtypes and restype to ensure correct data passing between Python and C.
  • For more complex C interfaces, consider using cffi which offers automatic header parsing and a cleaner API.

This technique lets you combine Python's ease of development with C's raw performance, giving the best of both worlds.

Navigation