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)
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.

🌐
Python
svn.python.org › projects › ctypes › trunk › ctypes › docs › manual › tutorial.html
ctypes tutorial
The same effect can be achieved ... 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 ...
🌐
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.
🌐
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]
🌐
Python
docs.python.org › 2.5 › lib › ctypes-passing-pointers.html
14.14.1.9 Passing pointers (or: passing parameters by reference)
December 23, 2008 - The same effect can be achieved ... 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 ...
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
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › python › using-pointers-in-python-using-ctypes
Using Pointers in Python using ctypes - GeeksforGeeks
July 23, 2025 - Notice one thing here we have used POINTER() instead of pointer that's because the pointer() function creates a new pointer instance, pointing to an object. In contrast, POINTER() is a factory function that creates and returns a new ctypes pointer type. Pointer types are cached and reused internally, the parameter which we will pass in POINTER() must be of ctypes.
🌐
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)).
🌐
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.
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
None is passed as a C NULL pointer, bytes objects and strings are passed as pointer to the memory block that contains their data (char* or wchar_t*). Python integers are passed as the platform’s default C int type, their value is masked to fit into the C type. Before we move on calling functions with other parameter types, we have to learn more about ctypes data types.
🌐
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
... int our_function(int num_numbers, int *numbers) { int i; int sum = 0; for (i = 0; i < num_numbers; i++) { sum += numbers[i]; } return sum; } ... import ctypes _sum = ctypes.CDLL('libsum.so') _sum.our_function.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_int)) def our_function(numbers): ...
Top answer
1 of 1
1

Listing:

  • [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) - a common pitfall when working with CTypes (calling functions)

  • [Python.Docs]: ctypes - A foreign function library for Python

Here's a piece of code exemplifying how to handle pointers and their addresses. The trick is to use ctypes.addressof (documented in the 2nd URL).

code00.py:

#!/usr/bin/env python

import ctypes as cts
import sys


CType = cts.c_float
CTypePtr = cts.POINTER(CType)


def ctype_pointer(seq):  # Helper
    CTypeArr = (CType * len(seq))
    ctype_arr = CTypeArr(*seq)
    return cts.cast(ctype_arr, CTypePtr)


def pointer_elements(addr, count):  # Helper
    return tuple(CType.from_address(addr + i * cts.sizeof(CType)).value for i in range(count))


def main(*argv):
    seq = (2.718182, -3.141593, 1.618034, -0.618034, 0)
    ptr = ctype_pointer(seq)
    print(f"Pointer: {ptr}")
    print(f"\nPointer elements: {tuple(ptr[i] for i in range(len(seq)))}")  # Check if pointer has correct data
    ptr_addr = cts.addressof(ptr.contents)  # @TODO - cfati: Straightforward
    print(f"\nAddress: {ptr_addr} (0x{ptr_addr:016X})\nElements from address: {pointer_elements(ptr_addr, len(seq))}")
    ptr_addr0 = cts.cast(ptr, cts.c_void_p).value  # @TODO - cfati: Alternative
    print(f"\nAddresses match: {ptr_addr == ptr_addr0}")


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

Notes:

  • Although it adds a bit of complexity, I introduced the CType "layer" to show that it should work with any type, not just float (as long as the values in the sequence are of that type)

  • The only truly relevant lines are those marked with @TODO

Output:

(py_pc064_03.08_test0_lancer) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q078366208]> python ./code00.py 
Python 3.8.19 (default, Apr  6 2024, 17:58:10) [GCC 11.4.0] 064bit on linux

Pointer: <__main__.LP_c_float object at 0x7203e97e7d40>

Pointer elements: (2.71818208694458, -3.1415929794311523, 1.6180340051651, -0.6180340051651001, 0.0)

Address: 125361127594576 (0x00007203E97A9A50)
Elements from address: (2.71818208694458, -3.1415929794311523, 1.6180340051651, -0.6180340051651001, 0.0)

Addresses match: True

Done.
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes.html
Ctypes - Thematic Tutorials
The key point is it must be a shared ... are passed by reference, that is as pointers or using byref. Also even though we used very simple data types, it is possible to deal with more complicated C structures. For this and more about ctypes see https://docs.python.org/3/lib...
🌐
Terryoy
terryoy.github.io › 2014 › 03 › using-ctypes-to-wrap-c-library.html
Use Ctypes to Wrap C Libraries in Python - Hello, World!
# it's a good practice to declare the structures and the functions at first like in C # ... # declare Structures class Context(Structure): pass ContextPtr = ctypes.POINTER(Context) # ...