2010-12-13 33 views
3

J'essayais d'utiliser le module ctypes pour un projet. Je créais un tableau dynamiquement alloué de paires "max_entries" et une fois le tableau épuisé, je créais un nouveau tableau de taille (1.5 * max_entries) et copiais le contenu de l'ancien tableau dans le nouveau tableau.Module ctypes Python: Accès au pointeur NULL lors de l'extension du tableau de pointeurs

Malheureusement, lorsque j'essaie d'accéder au contenu de cette nouvelle arborescence, j'obtiens une exception "NULL pointer access". Le code C correspondant semble fonctionner parfaitement. (Voir le code ci-dessous.)

Je me demandais s'il me manquait quelque chose sur le fonctionnement du module ctypes. Toute aide serait grandement appréciée. (Je ne sais pas si c'est la liste de diffusion appropriée pour ma question.)

/mercis!


#!/usr/bin/env python 

from ctypes import * 
import math 
import random 


class PAIR(Structure): 
    _fields_ = [("a", c_long), 
       ("b", c_long)] 


class MY_ARR(Structure): 
    _fields_ = [("no_entries", c_longlong), 
       ("max_entries", c_longlong), 
       ("entries", POINTER(POINTER(PAIR))) 
       ] 

def extendArray(x): 
    print "Extending Array" 
    print "Before: %d/%d" % (x.no_entries, x.max_entries) 
    old_arr = x.entries 

    # Create a new array 
    new_max_entries = int(math.ceil(1.5 * x.max_entries)) 
    x.entries = (POINTER(PAIR) * new_max_entries)() 

    # Copy the entries from the old array to the new array 
    for i in range(x.no_entries): 
     x.entries[i] = old_arr[i] 

    x.max_entries = new_max_entries 
    print "After: %d/%d" % (x.no_entries, x.max_entries) 
    return x 

def printPair(x): 
    print x.contents.a, x.contents.b 

def printArray(x): 
    print "Printing %d/%d Entries" % (x.no_entries, x.max_entries) 
    for i in range(x.no_entries): 
     printPair(x.entries[i]) 


if __name__ == "__main__": 
    x = MY_ARR(0, 10, (POINTER(PAIR) * 10)()) 
    for i in range(100): 
     if x.no_entries == x.max_entries: 
      print "\n\nPrinting Before Extension" 
      printArray(x) 

      extendArray(x) 

      print "\n\nPrinting After Extension" 
      printArray(x) 

     my_pair = PAIR(i, random.randint(0, 100)) 
     x.entries[x.no_entries] = pointer(my_pair) 
     x.no_entries += 1 

     printPair(x.entries[i]) 

    printArray(x) 

Maintenant, malheureusement, lorsque je tente d'exécuter ce code, je reçois une exception "accès pointeur NULL":

$ python TestExtension.py 
0 40 
1 40 
2 11 
3 36 
4 82 
5 73 
6 93 
7 100 
8 75 
9 80 


Printing Before Extension 
Printing 10/10 Entries 
0 40 
1 40 
2 11 
3 36 
4 82 
5 73 
6 93 
7 100 
8 75 
9 80 
Extending Array 
Before: 10/10 
After: 10/15 


Printing After Extension 
Printing 10/15 Entries 
Traceback (most recent call last): 
    File "TestExtension.py", line 55, in <module> 
    printArray(x) 
    File "TestExtension.py", line 42, in printArray 
    printPair(x.entries[i]) 
    File "TestExtension.py", line 37, in printPair 
    print x.contents.a, x.contents.b 
ValueError: NULL pointer access 

Les travaux de code C correspondant parfaitement:

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 

typedef struct { 
    long a; 
    long b; 
} pair; 

typedef struct { 
    long long no_entries; 
    long long max_entries; 
    pair **entries; 
} my_arr; 

my_arr *extend_array(my_arr *x) { 
    int i; 
    pair **old_entries = x->entries; 
    long long new_max_entries = ceil(1.5 * x->max_entries); 

    printf("Extending Array\n"); 
    printf("Before: %lld/%lld\n", x->no_entries, x->max_entries); 

    x->entries = malloc(sizeof(pair *) * new_max_entries); 
    for (i = 0; i < 100; ++i) { 
     x->entries[i] = old_entries[i]; 
    } 
    x->max_entries = new_max_entries; 
    free(old_entries); 

    printf("After: %lld/%lld\n", x->no_entries, x->max_entries); 
    return x; 
} 

void print_pair(pair *p) { 
    printf("%ld\t%ld\n", p->a, p->b); 
} 

void print_array(my_arr *x) { 
    int i; 
    printf("Printing %lld/%lld entries\n", x->no_entries, x->max_entries); 
    for (i = 0; i < x->no_entries; ++i) { 
     print_pair(x->entries[i]); 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    int i; 
    my_arr x = { 
     0, 
     10, 
     malloc(sizeof(pair *) * 10) 
    }; 

    for (i = 0; i < 100; ++i) { 
     if (x.no_entries == x.max_entries) { 
      extend_array(&x); 
     } 
     pair *my_pair = malloc(sizeof(pair)); 
     my_pair->a = i; 
     my_pair->b = rand() % 100; 

     x.entries[x.no_entries++] = my_pair; 
     print_pair(x.entries[i]); 
    } 
    print_array(&x); 
    return 0; 
} 

Répondre

2

Le problème est que la déclaration

old_arr = x.entries 

ne fait pas ce que vous attendez. Jetez un oeil à old_arr._b_base_, et vous verrez qu'il s'agit d'un pointeur pointant vers MY_ARR. Ainsi, lorsque le pointeur sous-jacent change, old_arr pointe soudainement vers le nouveau tableau, et la boucle assigne beaucoup de pointeurs NULL. Pour résoudre ce problème, écrivez

new_max_entries = int(math.ceil(1.5 * x.max_entries)) 
new_entries = (POINTER(PAIR) * new_max_entries)() 

# Copy the entries from the old array to the new array 
for i in range(x.no_entries): 
    new_entries[i] = x.entries[i] 

x.entries = new_entries 
x.max_entries = new_max_entries