Segfault при доступе к элементу структуры ctypes с инициализацией C - PullRequest
2 голосов
/ 18 августа 2011

Я пытаюсь получить доступ к элементам структуры из ctypes. Структура создается в функции init в коде C, и указатель на нее возвращается в Python. Проблема, с которой я сталкиваюсь, заключается в том, что при попытке получить доступ к элементам возвращаемой структуры возникает ошибка. Вот код:

Код C (который я назвал ctypes_struct_test.c):

#include <stdio.h>
#include <stdbool.h>

typedef struct {
    bool flag;
} simple_structure;

simple_structure * init()
{
    static simple_structure test_struct = {.flag = true};
    if (test_struct.flag) {
        printf("flag is set in C\n");
    }
    return &test_struct;
}

Код Python (который я назвал ctypes_struct_test.py):

#!/usr/bin/env python

import ctypes
import os

class SimpleStructure(ctypes.Structure):
    _fields_ = [('flag', ctypes.c_bool)]

class CtypesWrapperClass(object):

    def __init__(self):

        cwd = os.path.dirname(os.path.abspath(__file__))
        library_file = os.path.join(cwd,'libctypes_struct_test.so')

        self._c_ctypes_test = ctypes.CDLL(library_file)

        self._c_ctypes_test.init.restypes = ctypes.POINTER(SimpleStructure)
        self._c_ctypes_test.init.argtypes = []

        self.simple_structure = ctypes.cast(\
                self._c_ctypes_test.init(),\
                ctypes.POINTER(SimpleStructure))


a = CtypesWrapperClass()
print 'Python initialised fine'
print a.simple_structure.contents
print a.simple_structure.contents.flag

C компилируется под Linux следующим образом:

gcc -o ctypes_struct_test.os -c --std=c99 -fPIC ctypes_struct_test.c
gcc -o libctypes_struct_test.so -shared ctypes_struct_test.os

При запуске python ctypes_struct_test.py я получаю следующий вывод:

flag is set in C
Python initialised fine
<__main__.SimpleStructure object at 0x166c680>
Segmentation fault

Есть ли какая-то проблема с тем, что я пытаюсь сделать, или с тем, как я пытаюсь это сделать?

Ответы [ 2 ]

4 голосов
/ 18 августа 2011

Чтобы сделать это, замените неправильную строку

self._c_ctypes_test.init.restypes = ctypes.POINTER(SimpleStructure)

по правильной линии

self._c_ctypes_test.init.restype = ctypes.POINTER(SimpleStructure)

Также рассмотрите возможность удаления бессмысленного броска

self.simple_structure = ctypes.cast(
    self._c_ctypes_test.init(), ctypes.POINTER(SimpleStructure))

Кастуется с ctypes.POINTER(SimpleStructure) на ctypes.POINTER(SimpleStructure)!

1 голос
/ 18 августа 2011

Вы объявляете test_struct как статическую локальную переменную внутри вашей процедуры инициализации - я с подозрением отношусь к этому с первого взгляда.(Имейте в виду, мой C немного ржавый.)

Предполагается, что статические локальные переменные в C сохраняются при нескольких вызовах одной и той же функции, но их scope такой же, какавтоматическая локальная переменная - и возвращение указателя на автоматическую локальную переменную, безусловно, даст вам ошибку.Даже если бы это обычно работало, если бы вы вызывали эту функцию из того же модуля перевода в C, тот факт, что вы вызываете ее из Python (и что вы используете segfaulting, как только вы пытаетесь получить доступ к структуре), является краснымflag.

Вместо того, чтобы возвращать указатель на статическую переменную, попробуйте использовать malloc () в вашей процедуре инициализации и вернуть указатель, полученный из этого.

[[[ПРЕДУПРЕЖДЕНИЕ: это приведет к утечке памяти.]]]

Но это нормально;если ошибка исчезнет, ​​вы поймете, что это была проблема.Затем вы можете исправить утечку памяти, предоставив в подпрограмме C вторую подпрограмму, которая вызывает free (), и убедитесь, что вы вызываете ее из кода Python, когда закончите со структурой.

...