Python передает 32-битный адрес указателя на функции C

Я хотел бы назвать свои C-функции в общей библиотеке из сценариев Python. Проблема возникает при передаче указателей, 64-битные адреса, по-видимому, усекаются до 32-битных адресов внутри вызываемой функции. И Python, и моя библиотека 64 бит.

Примеры примеров ниже демонстрируют проблему. Сценарий Python печатает адрес данных, передаваемых функции C. Затем полученный адрес печатается из вызываемой функции C. Кроме того, функция C доказывает, что она 64-битная, печатая размер и адрес локально создающей памяти. Если указатель используется любым другим способом, результатом является segfault.

CMakeLists.txt

cmake_minimum_required (VERSION 2.6) add_library(plate MODULE plate.c) 

plate.c

 #include <stdio.h> #include <stdlib.h> void plate(float *in, float *out, int cnt) { void *ptr = malloc(1024); fprintf(stderr, "passed address: %p\n", in); fprintf(stderr, "local pointer size: %lu\n local pointer address: %p\n", sizeof(void *), ptr); free(ptr); } 

test_plate.py

 import numpy import scipy import ctypes N = 3 x = numpy.ones(N, dtype=numpy.float32) y = numpy.ones(N, dtype=numpy.float32) plate = ctypes.cdll.LoadLibrary('libplate.so') print 'passing address: %0x' % x.ctypes.data plate.plate(x.ctypes.data, y.ctypes.data, ctypes.c_int(N)) 

Выход из python-2.7

В [1]: run ../test_plate.py

адрес: 7f9a09b02320

адрес: 0x9b02320

размер локального указателя: 8

адрес локального указателя: 0x7f9a0949a400

4 Solutions collect form web for “Python передает 32-битный адрес указателя на функции C”

Проблема в том, что модуль ctypes не проверяет подпись функции функции, которую вы пытаетесь вызвать. Вместо этого он основывает типы C на типах Python, поэтому строка …

 plate.plate(x.ctypes.data, y.ctypes.data, ctypes.c_int(N)) 

… передает первые два параметра как целые числа. См. Ответ eryksun по причине, почему они усекаются до 32 бит.

Чтобы избежать усечения, вам нужно указать ctypes что эти параметры на самом деле указывают на что-то вроде …

 plate.plate(ctypes.c_void_p(x.ctypes.data), ctypes.c_void_p(y.ctypes.data), ctypes.c_int(N)) 

… хотя то, на что они на самом деле указывают, – другое дело – они не могут быть указателями на float как предполагает ваш код на C.


Обновить

С тех пор eryksun опубликовал гораздо более полный ответ для примера numpy specific в этом вопросе, но я оставлю это здесь, поскольку он может быть полезен в общем случае усечения указателя для программистов, использующих что-то другое, кроме numpy .

PyIntObject Python использует внутреннюю часть C, которая является 64-разрядной на большинстве 64-разрядных платформ (исключая 64-разрядную Windows). Однако ctypes присваивает преобразованный результат pa->value.i , где value является объединением, а поле i – 32-битным int . Подробности см. В ConvParam в модулях / _ctypes / callproc.c , строках 588-607 и 645-664. ctypes был разработан в Windows, где long всегда 32-битный, но я не знаю, почему это не было изменено для использования long поля, то есть pa->value.l . Наверное, в большинстве случаев удобнее всего использовать C int вместо использования полного диапазона long .

В любом случае, это означает, что вы не можете просто передать Python int для создания 64-битного указателя. Вы должны явно создать указатель ctypes. У вас есть несколько вариантов для этого. Если вас не интересует безопасность типов, самым простым вариантом для массива NumPy является использование его атрибута ctypes . Это определяет _as_parameter_ который позволяет объектам Python устанавливать, как они преобразуются в вызовы функций ctypes (см. Строки 707-719 в предыдущей ссылке). В этом случае создается void * . Например, вы бы назвали plate следующим образом:

 plate.plate(x.ctypes, y.ctypes, N) 

Тем не менее, это не предлагает никакой безопасности типов, чтобы предотвратить вызов функции с помощью массива неправильного типа, что приведет либо к ошибкам, либо к ошибкам сегментации, либо к ошибкам сегментации. np.ctypeslib.ndpointer решает эту проблему. Это создает настраиваемый тип, который можно использовать при настройке argtypes и restype указателя функции ctypes. Этот тип может проверять тип данных массива, количество измерений, форму и флаги. Например:

 import numpy as np import ctypes c_npfloat32_1 = np.ctypeslib.ndpointer( dtype=np.float32, ndim=1, flags=['C', 'W']) plate = ctypes.CDLL('libplate.so') plate.plate.argtypes = [ c_npfloat32_1, c_npfloat32_1, ctypes.c_int, ] N = 3 x = np.ones(N, dtype=np.float32) y = np.ones(N, dtype=np.float32) plate.plate(x, y, N) # the parameter is the array itself 

Если вы не укажете ctypes, какой тип параметров, он пытается вывести его из значений, которые вы передаете функции. И этот вывод не всегда будет работать так, как вам нужно.

Рекомендуемый способ справиться с этим – установить атрибут argtypes функции и так явно указать ctypes какие типы параметров.

 plate.plate.argtypes = [ ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float), ctypes.c_int ] 

Затем вы можете вызвать функцию следующим образом:

 plate.plate(x.ctypes.data, y.ctypes.data, N) 

На самом деле, вы должны установить plate.argstype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int] , и тогда будет нормально принимать адрес в c func из python.
Я встретил проблему, и я решил ее как то, что я говорю.

  • Модули устанавливаются с помощью pip в OSX, но не отображаются при импорте
  • Python3, lxml и «Symbol not found: _lzma_auto_decoder» в Mac OS X 10.9
  • записать выходной звук в python
  • Подключение к сети SMB на рабочем столе
  • Как я могу найти полный путь к шрифту из его отображаемого имени на Mac?
  • В Mac OS X вы используете отправленный питон или свой собственный?
  • pip3 install pyautogui не работает с кодом ошибки 1 Mac OS
  • Может ли python отправлять текст в буфер обмена Mac
  • Python - лучший язык программирования в мире.