开发者

Python ctypes module: NULL pointer access while extending pointer array

I was trying to use the ctypes module for a project. I was creating a dynamically allocated array of "max_entries" pairs and once the array was exhausted, I was creating a new array of size (1.5 * max_entries) and copying the contents from the old array to the new array.

Unfortunately, when I try to access the contents of this new_array, I get a "NULL pointer access" exception. The corresponding C code seems to work perfectly. (See code below.)

I was wondering if I was missing something about the way the ctypes module works. Any help would be greatly appreciated. (Not sure if this is the appropriate mailing list for my question.)

/Thanks!


#!/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)

Now unfortunately, when I try to run this code, I am getting a "NULL pointer access" exception:

$ 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

The corresponding C code works perfectly:

#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;
}


The problem is that the statement

old_arr = x.entries

doesn't do what you expect it to. Take a look at old_arr._b_base_, and you see that it is a pointer pointing into MY_ARR. So when the underlying pointer changes, old_arr suddenly points to the new array, and the loop assigns lots of null pointers. To fix this, write

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜