You can either create a c_float instance and initialize with a pointer to that instance, or create a c_float array and pass it, which in ctypes imitates a decay to a pointer to its first element.

Note that ctypes.pointer() creates pointers to existing instances of ctypes objects while ctypes.POINTER() creates pointer types.

test.c - for testing

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct Input {
    float* number1;
    float  number2;
} Input;

API void expose_init(Input* input) {
    printf("%f %f\n",*input->number1, input->number2);
}

test.py

import ctypes

class input_struct (ctypes.Structure):
    _fields_ = (('number1', ctypes.POINTER(ctypes.c_float)),
                ('number2', ctypes.c_float))

c_instance = ctypes.CDLL('./test')
init_func = c_instance.expose_init
# Good habit to fully define arguments and return type
init_func.argtypes = ctypes.POINTER(input_struct),
init_func.restype = None

inp_str_ptr = input_struct()
num = ctypes.c_float(20)     # instance of c_float, similar to C "float num = 20;"
inp_str_ptr.number1 = ctypes.pointer(num) # similar to C "inp_str_ptr.number1 = #"
inp_str_ptr.number2 = 100

c_instance.expose_init(ctypes.byref(inp_str_ptr))

# similar to C "float arr[1] = {30}; inp_str_ptr = arr;"
inp_str_ptr.number1 = (ctypes.c_float * 1)(30)
c_instance.expose_init(ctypes.byref(inp_str_ptr))

Output:

20.000000 100.000000
30.000000 100.000000
Answer from Mark Tolonen on Stack Overflow
🌐
ProgramCreek
programcreek.com › python › example › 1881 › ctypes.c_float
Python Examples of ctypes.c_float
Parameters ---------- handle : cudnnHandle Handle to a previously created cuDNN context. srcDesc : cudnnTensorDescriptor Handle to a previously initialized tensor descriptor. srcData : void_p Pointer to data of the tensor described by srcDesc descriptor. value : float Value that all elements of the tensor will be set to. """ dataType, _, _, _, _, _, _, _, _ = cudnnGetTensor4dDescriptor(srcDesc) if dataType == cudnnDataType['CUDNN_DATA_DOUBLE']: alphaRef = ctypes.byref(ctypes.c_double(alpha)) else: alphaRef = ctypes.byref(ctypes.c_float(alpha)) status = _libcudnn.cudnnSetTensor(handle, srcDesc, srcData, alphaRef) cudnnCheckStatus(status)
🌐
Fz-juelich
pgi-jcns.fz-juelich.de › portal › pages › using-c-from-python.html
Using C from Python: How to create a ctypes wrapper - Scientific IT-Systems
Thanks to that, it is also possible to wrap a Python function in a way that it becomes callable from C. When dealing with APIs that include events (e.g. a GUI library), this comes in very helpful, as it allows us to use Python functions as C callbacks. To do this, we need to create a function type with the method ctypes.CFUNCTYPE: callback_type = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_float, ctypes.c_float)
🌐
Stack Overflow
stackoverflow.com › questions › 60788664 › difficulty-returning-a-float-from-ctypes
python - Difficulty returning a float from Ctypes - Stack Overflow
Of interest, when I use the np.ctypeslib.ndpointer method of defining restypes, the python type function still claims the result is a numpy.ndarray but will complain if you treat it like one. I've tried alternatively defining the restypes of this function or variations on the function as ctypes.c_float*length or ctypes.POINTER(ctypes.c_float*length) but with no success.
Top answer
1 of 2
3

Dealing with pointers and arrays is explained in [Python.Docs]: ctypes - Type conversions.

I prepared a dummy example for you.

main00.c:

#if defined(_WIN32)
#  define DECLSPEC_DLLEXPORT __declspec(dllexport)
#else
#  define DECLSPEC_DLLEXPORT
#endif


static int kSize = 5;


DECLSPEC_DLLEXPORT int size() {
    return kSize;
}


DECLSPEC_DLLEXPORT int function(int dummy, float *data1, float *data2) {
    for (int i = 0; i < kSize; i++) {
        data1[i] = dummy * i;
        data2[i] = -dummy * (i + 1);
    }
    return 0;
}

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct


c_float_p = ct.POINTER(ct.c_float)


def main(*argv):
    dll = ct.CDLL("./dll00.so")

    size = dll.size
    size.argtypes = []
    size.restype = ct.c_int


    function = dll.function
    function.argtypes = [ct.c_int, c_float_p, c_float_p]
    function.restype = ct.c_int

    sz = size()
    print(sz)

    data1 = (ct.c_float * sz)()
    data2 = (ct.c_float * sz)()

    res = function(1, ct.cast(data1, c_float_p), ct.cast(data2, c_float_p))
    for i in range(sz):
        print(data1[i], data2[i])


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Notes:

  • The C part tries to mimic what your .dll does (or at least what I understood):
    • size - gets the arrays sizes
    • function - populates the arrays (till their size - assuming that they were properly allocated by the caller)
  • Python part is straightforward:
    • Load the .dll
    • Define argtypes and restype (in your code it's restype's) for the 2 functions (for size_func not necessary)
    • Get the lengths
    • Initialize the arrays
    • Pass them to function_func using ctypes.cast

Output (on Lnx, as building the C code is much simpler, but works on Win as well):

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> gcc -shared -o dll00.so main00.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> python3 code00.py
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux

5
0.0 -1.0
1.0 -2.0
2.0 -3.0
3.0 -4.0
4.0 -5.0
2 of 2
0

It really depends on what you are doing with these float pointers.

If you are trying to traverse it, i.e.

for(int i = 0; i < size; i++)
    printf("%f\n%, data1[i])

then for sure this is problematic as no array was allocated. You simply passed a pointer pointing to a float. That is all.

You need to first allocate that memory. To this end Attempt 4 looks like the more promising, but I suspect you have a problem inside your C function leading to the crash.

Difficult to say without seeing the implementation of that function.

🌐
Causeway
cvstuff.wordpress.com › 2014 › 11 › 27 › wraping-c-code-with-python-ctypes-memory-and-pointers
Wraping C code with Python CTypes: memory and pointers | Causeway
November 28, 2014 - After compiling this library with g++ or gcc (see my last post), I declare to Python using CTypes: # the fillprototype is a user-defined function, see my previous post... fillprototype(mylib.add_vector, ctypes.POINTER(ctypes.c_double), [ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.c_int]) We can pass numpy variables to this function by casting them as CTypes: >> a = np.array([1,2,3], dtype=float) >> b = np.array([4,5,6], dtype=float) >> p_a = a.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) >> p_b = b.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) >> d = ctypes.c_int(len(a)) >> c = mylib.dot_product(p_a, p_b, d) Here the variable ‘c’ refer to a memory block allocated on the C side.
Top answer
1 of 2
2

Here is a minimal example to pass C float as a parameter using numpy. It is an exercise for the OP to apply it to their function. Note that the C function must assume a fixed size or be passed the length of the array in some form.

Also note that the dtype is declared as ct.c_float since Python float is typically 64-bit and C float is typically 32-bit. Make sure the type sizes agree.

test.c

#include <stdio.h>

__declspec(dllexport) // for Windows
void func(float* p, size_t size) {
    for(size_t i = 0; i < size; ++i)
        printf("p[%zu] = %f\n", i, p[i]);
}

test.py

import ctypes as ct
import numpy as np

dll = ct.CDLL('./test')
# The helper function "ndpointer" can declare the expected type
# and either number of dimensions expected or the shape of the
# numpy array.  ctypes will then require that array and type check
# the parameter. 
dll.func.argtypes = np.ctypeslib.ndpointer(dtype=ct.c_float, ndim=1),
dll.func.restype = None

x_spots = np.arange(-1, 2, 1, dtype=ct.c_float)
dll.func(x_spots, len(x_spots))

Output:

p[0] = -1.000000
p[1] = 0.000000
p[2] = 1.000000
2 of 2
1

One problem I can spot in your code is the size of your floats.

Note that it doesn't really matter as a pointer to what you pass pointers to your numpy data to your function. I, for one, am used to pass data as arr.ctypes.data_as(ctypes.c_void_p).

This is dynamic, so it is not like C compiler could check anything. And, at least in this case, there is no dynamic checking neither that in makes senses to consider this numpy array data as a pointer to this type.

So, saying correctly or not as POINTER to what you want to pass a numpy array to a function that expect a float * doesn't protect you against error.

It doesn't neither imply any conversion. The pointer is just passed to the C function, it is up to it to interpret correctly, and that has been done statically, when you typed the parameter float *.

So, it is not there (in the code you seem to be wondering about) that you can ensure that the C function get the bunch of float it expect. Whatever, the C function will get a pointer to the numpy data.

The problem you have in your code is that data in x_spots are not float (as in the C meaning of the word float; that is 32 bits floating point numbers). They are most likely double

I say "most likely" because I am not an expert in different python interpreters. I know that, depending on the interpreter, native float type of python may be float32, or float64 or even something else. In the most classical CPython, they are float64. And I am not sure neither what it means for numpy when dtypes is the native type float. But well, with my cpython, a np.array([1,2,3], dtype=float) is a numpy array made of float64. So, not 100% sure if that is a sure thing, or if it exists python interpreter in which that array would be made of float32. It is because I am unsure of that, that, anyway, I never ever use float as an argument for dtype. I use np.float32 or np.float64.

But, well, most likely (or even surely) your x_spots is made of float64.

And then, it doesn't matter a pointer to what you say it is when you pass x_spots.ctypes.data_as(POINTER(...)), the pointer will be a pointer to those float64. That is, in C wording, to double. And your C function will treat it as a pointer to float (with no warning whatsoever: warning occur at compilation time, and we are past that).

So, long story. But conclusion is, either you

  • Change your C function to accept double * as parameter for this x_spots thing. That's probably the best way, with your, probably 64 bits computer. But you seem to say that you can't really modify the C function
  • So, alternatively, you can ensure that your data is made of float (in C meaning), that is create your x_spots with a dtype=np.float32.

Note that even if you choose the first case (double *), it would be better to change also dtype to explicitly set it to np.float64.

And once you have done that, it doesn't matter if you pass your argument as x_spots.ctypes.data_as(c_void_p), as x_spots.ctypes.data_as(POINTER(c_float)), as x_spots.ctypes.data_as(POINTER(c_double)), or even x_spots.ctypes.data_as(POINTER(c_char)).

(I strongly encourage you, of course, not to use a false type as POINTER() arg, but that is for human readability of your code; from execution change point of view, it wouldn't change the result)

So tl;dr

  • Change type of parameter of C function from float * to double *
  • Or, change dtype of your numpy arrays from float to np.float32
🌐
Python
svn.python.org › projects › ctypes › trunk › ctypes › docs › manual › tutorial.html
ctypes tutorial
ctypes exports the byref function which is used to pass parameters by reference. The same effect can be achieved with the pointer function, although pointer does a lot more work since it constructs a real pointer object, so it is faster to use byref if you don't need the pointer object in Python itself: >>> i = c_int() >>> f = c_float() >>> s = create_string_buffer('\000' * 32) >>> print i.value, f.value, repr(s.value) 0 0.0 '' >>> libc.sscanf("1 3.14 Hello", "%d %f %s", ...
Find elsewhere
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
Pointer instances have a contents attribute which returns the object to which the pointer points, the i object above: ... Note that ctypes does not have OOR (original object return), it constructs a new, equivalent object each time you retrieve an attribute:
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes.html
Ctypes - Thematic Tutorials
which you want to call from python. First make a shared object library by doing (at the command line) ... Note that on OSX -shared should be replaced by -dynamiclib and sum.so should be called sum.dylib Then you can do · from ctypes import * my_sum=CDLL('sum.so') a=numpy.array(range(10),dtype=float) my_sum.sum(a.ctypes.data_as(c_void_p),int(10)) Note here that a.ctypes.data_as(c_void_p) returns a ctypes object that is void pointer to the underlying array of a.
🌐
Microsoft Community
techcommunity.microsoft.com › microsoft community hub › communities › topics › microsoft learn › microsoft learn
how to pass float* array to C method from python script whose memory allocation takes place in C lay | Microsoft Community Hub
October 12, 2020 - TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCPointerType' this is the error I get when I run the script. I am little confused on how to send argument for float *oresults in script.
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.ndarray.ctypes.html
numpy.ndarray.ctypes — NumPy v2.5 Manual
For example, calling self._as_parameter_ is equivalent to self.data_as(ctypes.c_void_p). Perhaps you want to use the data as a pointer to a ctypes array of floating-point data: self.data_as(ctypes.POINTER(ctypes.c_double)).
🌐
SciPy
scipy.github.io › old-wiki › pages › Cookbook › Ctypes.html
Cookbook/Ctypes - SciPy wiki dump
By allocating your buffers as NumPy arrays, the Python garbage collector can take care of this. ... 1 from ctypes import * 2 ALLOCATOR = CFUNCTYPE(c_long, c_int, POINTER(c_int)) 3 # load your library as lib 4 lib.baz.restype = None 5 lib.baz.argtypes = [c_float, c_int, ALLOCATOR]
🌐
GeeksforGeeks
geeksforgeeks.org › python › using-pointers-in-python-using-ctypes
Using Pointers in Python using ctypes - GeeksforGeeks
July 23, 2025 - Since in Python the concept of pointers is not properly available or used we can't properly create a Void Pointer, as the POINTER function requires one argument i.e the data type. But we can create a Void pointer of a certain data type and then don't point it to any memory address. We are just typecasting it like C/C++ early. We can also create some void pointers of a specific type that doesn't hold any address while creating. ... import ctypes as ct # Creating a variable ptr2 which will act as a void pointer # We will not point it to any other memory location ptr2 = ct.POINTER(ct.c_int) # Trying to print a void pointer print(ptr2)
🌐
GitHub
gist.github.com › lud4ik › 3403220
How to wrap C code in Python (ctypes version) · GitHub
Syntax Error yerr=np.array([sig_y]*N,'float64') yerr_p=y.ctypes.data_as(POINTER(c_double)) last line is supposed to be: yerr_p=yerr.ctypes.data_as(POINTER(c_double)) Maybe you just copy/paste the previous pointer assignment · Sign up for free to join this conversation on GitHub.
🌐
Dalkescientific
dalkescientific.com › writings › NBN › ctypes.html
ctypes
The ctypes interface by default only handles Python integer, long, and string data types. If you use float or something else then you must tell ctypes how to convert the C function call arguments and result value.