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
精彩评论