Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Real Pointers With ctypes

In this lesson, you’ll learn that it is possible to create pointers in Python, but you need to use a bit of C. Sometimes, you may want to improve performance by using a C library in your Python code. For this lesson, you’ll be using the built-in ctypes module to create real C-style pointers in Python.

00:00 You may run into a scenario where you want to utilize a C library in your Python code, or maybe you want to write a performance-critical function of your application in C, and you still want to be able to access it from Python.

00:16 This is where the ctypes module comes in. The built-in ctypes module allows you to interface with C libraries. It provides all the necessary Python required to do so, including the ability to create pointers for C functions that require them.

00:34 This is probably the closest thing you’ll get to real pointers in Python. Let’s create a C function, then utilize it within the Python interactive shell.

00:45 To create a new C file I’m going to use VIM, but you can use any text editor you’d like. A new C file can be created with vim add.c.

00:59 I’m going to write a very small function that will take a pointer to an integer and increment the value it points to by 1. First, I need to tell VIM that I want to edit the current file. So I’ll hit i to go into insert mode.

01:15 Now I can write the function which will return nothing and have a name of add_one. It should take a pointer to an integer, so I should be sure the parameter is int*.

01:30 Now I can dereference the parameter with the & (dereference) operator and increment the value by 1. This is a trivial example of a C function, similar to what you saw before, but it should do. Before, we compiled and linked our C code into an executable to run it directly, but in order to use it in Python, we need to compile it into a library.

01:55 I will exit VIM with Escape then :wq + Enter, which will save and quit. The quickest way to compile the code is with GCC, or the GNU Compiler Collection.

02:12 This is the same tool we use to compile our C code earlier in the course. The command we need now is gcc -c -fpic add.c.

02:27 This will compile the code into an object file, which is our program written in machine code. We’ll need one more GCC command, which will take that object file and convert it into a dynamic library for use with our Python program.

02:46 The name of that library file it outputs will be libadd1.so, short for “shared objects.” Now that we’ve got a shared object file compiled from our C code, we can fire up a new Python interactive shell and try it out.

03:04 Of course, this will also work from a Python script. I’ve recently started using bpython in my courses, which gives me a nice colored output, that’s a little bit easier to read.

03:15 The first thing we need to do is import the ctypes module. It’s built-in with

03:22 the Python standard library, so we don’t need to download it. Then, we can create a new Python object with the CDLL function. That looks like this.

03:36 This path here should point to the shared object file we just created with gcc.

03:43 The ./ tells Python to search for the library in the current directory we’re working in. Now that Python knows about the C library we wrote, we can store a reference to the add_one function in another Python object.

03:59 I’ll call it, well, add_one.

04:05 At this point, add_one acts sort of like a Python function. We can call it with parentheses, and supply arguments, and it will call the underlying C function. But before we do that, we should specify what parameters are required for this C function.

04:23 This will allow Python to stop us if we pass in the wrong type of argument. We can do that with the argtypes attribute of the add_one object.

04:35 The C function takes an integer pointer. So I’ll say, ctypes.POINTER and pass in ctypes.c_int. To show you what this actually did, let me try calling the add_one function, but with a regular integer, instead of a pointer to one. You see Python raises a ctypes.ArgumentError.

05:01 It says expected LP_c_int, which means integer pointer, but instead, we supplied just a regular integer. Python was able to stop execution before we ever even called the underlying C function, and that’s because we specified the parameter types for this function. To create a pointer to an integer like the function is expecting, we first need to create a C-style integer.

05:31 That can be done with ctypes.c_int.

05:36 As you can see, this new C integer has a value of 0. Now we can pass a pointer, or a reference, to the x object in memory. I’ll call add_one, and then to obtain a pointer to the x object I’ll use the byref function.

05:59 There we go. And just to be sure it worked, we can check the value of x. Now you’ve seen how you can pass a pointer to a C function from within Python.

06:11 Keep in mind, this function didn’t return anything. It merely modified the integer at the memory address we supplied, which works because C integers are immutable.

Avatar image for Ian Christy

Ian Christy on March 9, 2021

REPL behaved slightly differently …

Python 3.8.8 (default, Feb 27 2021, 02:19:17) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> add_lib = ctypes.CDLL("./libadd1.so")
>>> add_one = add_lib.add_one
>>> add_one.argtypes = [ctypes.POINTER(ctypes.c_int)]
>>> x = ctypes.c_int()
>>> add_one(ctypes.byref(x))
266098448
>>> x
c_int(1)
>>> 
Avatar image for Jordan Bell

Jordan Bell on Aug. 10, 2024

The same issue as Ian’s March 2021 post

Become a Member to join the conversation.