代码我也放在了gist上 https://gist.github.com/hit9/7244344
foolib.c:
typedef struct post {
char *x;
char *y;
int x_z;
} post_t;
void
foo (post_t *o, char *src)
{
int i=0;
char *p = src;
int len = strlen(src);
for (; *p != '\0'; i++, p++) {
if (*p == 'x') {
o->x = p;
o->x_z = len-i;
}
}
o->y=src;
}
test.py:
from ctypes import *
foolib = CDLL("./foolib.so")
class Post(Structure):
_fields_ = (
("x", c_void_p),
("y", c_void_p),
("x_z", c_int),
)
o = Post()
s = "iooxooiddfggggggggggggvd"
foolib.foo(byref(o), create_string_buffer(s))
print o.x_z
print string_at(o.x, o.x_z)
print string_at(o.y, len(s))
其中,foolib.c用来生成一个动态链接库 foolib.so
打印结果为何是:
foo ➤ python test.py
21
ooiddfggggggggggggvd
ooiddfggggggggggggvd
而不是
21
iooxooiddfggggggggggggvd
ooiddfggggggggggggvd
呢?
赞代码有可下载的 gist 版本!
我这边的结果和你的是不一样的。
>>> python2 test.py
src: iooxooiddfggggggggggggvd
x: xooiddfggggggggggggvd, y: iooxooiddfggggggggggggvd
21
dfggggggggggggvd
dfggggggggggggvd
>>> python3 test.py
src: iooxooiddfggggggggggggvd
x: xooiddfggggggggggggvd, y: iooxooiddfggggggggggggvd
21
b'\x00\x00\x00\x00\x00\xc0+\x8d\xa2\xdf\x7f\x00\x00ggggggvd'
b'\x00\x00\x00\x00\x00\x00\x00\x00\xc0+\x8d\xa2\xdf\x7f\x00\x00ggggggvd'
从 Python 3 的返回结果中,我们很容易看出来,部分内存被回收了。实际上 Python 2 版本在最后的 print
的值加上 repr
调用的话也很明显:
21
'\x00\x00\x00\x00\x00dfggggggggggggvd'
'\x00\x00\x00\x00\x00\x00\x00\x00dfggggggggggggvd'
因为那些值都是 NUL
,所以输出了也看不到而已。
于是,你知道啦,你创建的那个 string buffer 被回收掉了:
foolib.foo(byref(o), create_string_buffer(s))
这句改成这样子就好了:
buf = create_string_buffer(s)
foolib.foo(byref(o), buf)
最终结果是这样子的:
src: iooxooiddfggggggggggggvd
x: xooiddfggggggggggggvd, y: iooxooiddfggggggggggggvd
21
xooiddfggggggggggggvd
iooxooiddfggggggggggggvd
因为o->x
是野指针:create_string_buffer(s)
返回的临时对象在foo
调用结束以后就被回收了。