Question: Consider this simple C source code which computes the mean of an array of int, stores it in a structure and returns an error code: #include <stdio.h> enum errors { NO_ERRORS, EMPTY_ARRAY }; struct Result { double mean; }; enum errors calculateMean(struct Result *result, int *array, int length) { if (length == 0) { return EMPTY_ARRAY; // Return EMPTY_ARRAY if the array is empty } int sum = 0; for (int i = 0; i < length; i++) { sum += array[i]; } result->mean = (double)sum / length; return NO_ERRORS; // Return NO_ERRORS if calculation is successful }  The code is compiled to a shared library named libtest_ctypes.so. I have the following ctypes interface: import ctypes import enum import numpy as np import enum lib_path = "./libtest_ctypes.so" lib = ctypes.CDLL(lib_path) # The Result structure class Result(ctypes.Structure): _fields_ = [("mean", ctypes.c_double)] # The Errors enum class Errors(enum.IntEnum): NO_ERRORS = 0 EMPTY_ARRAY = 1 # Defining a signature for `calculateMean` calculate_mean = lib.calculateMean calculate_mean.argtypes = [ctypes.POINTER(Result), ctypes.POINTER(ctypes.c_int), ctypes.c_int] calculate_mean.restype = Errors # Runs `calculateMean` def calculate_mean_interface(array): result = Result() length = len(array) c_array = array.ctypes.data_as(ctypes.POINTER(ctypes.c_int)) error = calculate_mean(ctypes.byref(result), c_array, length) return error, result if __name__ == "__main__": array = np.array([1, 2, 3, 4, 5]) error, result = calculate_mean_interface(array) if error == Errors.EMPTY_ARRAY: print("Error: Empty array!") elif error == Errors.NO_ERRORS: print("Mean:", result.mean)  Running the python interface gives a wrong result 1.2. To my undestanding this is due to a difference in types between the numpy array (64 bit integeres) and the C's int on my machine. I can get the right result, 3.0, casting the array to ctype.c_int through numpy's .astype(): def calculate_mean_interface(array): result = Result() length = len(array) #** CAST ARRAY TO CTYPES.C_INT** array = array.astype(ctypes.c_int) c_array = array.ctypes.data_as(ctypes.POINTER(ctypes.c_int)) error = calculate_mean(ctypes.byref(result), c_array, length) return error, result  However numpy's casting requires extra memory and time. What is the best way to achieve a correct result without casting? I'd like this to be portable and, if possible, I don't want to specify a dtype when initializing the numpy array.

Answer: According to [NumPy]: Scalars - class numpy.int_ (emphasis is mine):  Signed integer type, compatible with Python int and C long.  So, you'll have to use that in all the places (and be consistent), otherwise you'll get Undefined Behavior. In your case (Little Endian Nix OS (and Python build)), the memory layout is (representation in HEX (2 digits per Byte)): NumPy array - 5 064bit (long) items: (1 + 2 + 3 + 4 + 5) / 5 = 3.0 ╔══════L0══════╗╔══════L1══════╗╔══════L2══════╗╔══════L3══════╗╔══════L4══════╗ 10000000000000002000000000000000300000000000000040000000000000005000000000000000 ╚══I0══╝╚══I1══╝╚══I2══╝╚══I3══╝╚══I4══╝ ... C array (same memory) - 5 032bit (int) items: (1 + 0 + 2 + 0 + 3) / 5 = 1.2  Check [SO]: Maximum and minimum value of C types integers from Python (@CristiFati's answer) for more details on integer types. Here's a working version.  dll00.c: #include <stdio.h> #if defined(_WIN32) # define DLL00_EXPORT_API __declspec(dllexport) #else # define DLL00_EXPORT_API #endif typedef enum { SUCCESS = 0, EMPTY_ARRAY, NULL_POINTER, UNSPECIFIED, } Errors; typedef struct { double mean; } Result; #if defined(__cplusplus) extern "C" { #endif DLL00_EXPORT_API Errors calculateMean(Result *result, long *array, int length); #if defined(__cplusplus) } #endif Errors calculateMean(Result *result, long *array, int length) { printf("From C - element size: %zu\n", sizeof(long)); if (length == 0) { return EMPTY_ARRAY; } if ((result == NULL) || (array == NULL)) { return NULL_POINTER; } long sum = 0; for (int i = 0; i < length; ++i) { sum += array[i]; } result->mean = (double)sum / length; return SUCCESS; }   code00.py: #!/usr/bin/env python import ctypes as cts import enum import sys import numpy as np class Result(cts.Structure): _fields_ = ( ("mean", cts.c_double), ) class Errors(enum.IntEnum): SUCCESS = 0, EMPTY_ARRAY = 1, NULL_POINTER = 2, UNSPECIFIED = 3, DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so") calculate_mean = None # Initialize it in main def calculate_mean_interface(array): result = Result() length = len(array) c_array = array.ctypes.data_as(cts.POINTER(cts.c_long)) error = calculate_mean(cts.byref(result), c_array, length) return error, result def main(*argv): dll = cts.CDLL(DLL_NAME) global calculate_mean calculate_mean = dll.calculateMean calculate_mean.argtypes = (cts.POINTER(Result), cts.POINTER(cts.c_long), cts.c_int) calculate_mean.restype = Errors array = np.array([1, 2, 3, 4, 5]) print(f"NP array type: {array.dtype}") error, result = calculate_mean_interface(array) if error != Errors.SUCCESS: print(f"Error: {error}") else: print(f"Mean: {result.mean}") 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)    Output:  Nix (Linux):  [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q077965251]> . ~/sopr.sh ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [064bit prompt]> [064bit prompt]> ls code00.py dll00.c [064bit prompt]> [064bit prompt]> gcc -fPIC -shared -o dll00.so dll00.c [064bit prompt]> ls code00.py dll00.c dll00.so [064bit prompt]> [064bit prompt]> /home/cfati/Work/Dev/VEnvs/py_pc064_03.10_test0/bin/python ./code00.py Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] 064bit on linux NP array type: int64 From C - element size: 8 Mean: 3.0 Done.    Win:  [cfati@CFATI-W10PC064:e:\Work\Dev\StackExchange\StackOverflow\q077965251]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> [prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul [prompt]> dir /b code00.py dll00.c dll00.so [prompt]> cl /nologo /MD /DDLL dll00.c /link /NOLOGO /DLL /OUT:dll00.dll dll00.c Creating library dll00.lib and object dll00.exp [prompt]> dir /b code00.py dll00.c dll00.dll dll00.exp dll00.lib dll00.obj dll00.so [prompt]> [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py Python 3.10.10 (tags/v3.10.10:aad5f6a, Feb 7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)] 064bit on win32 NP array type: int32 From C - element size: 4 Mean: 3.0 Done.     Might also want to check:  [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)  [SO]: Sending numpy array (image) to c++ library with ctypes (@CristiFati's answer)  [SO]: Boundary problems using a numpy kernel and padding on a matrix within a C functions called from python (@CristiFati's answer)  [SO]: How to run a Fortran script with ctypes? (@CristiFati's answer)  [SO]: _csv.Error: field larger than field limit (131072) (@CristiFati's answer)