パスワードを忘れた? アカウント作成
30028 journal

greenteaの日記: python ctypes使ってみた。

日記 by greentea

Pythonには、Cのライブラリを読み込んで使える「ctypes」とかいう便利なライブラリがあるらしい。
WindowsでもLinuxでも使えるみたい。

http://starship.python.net/crew/theller/ctypes/
http://ymasuda.jp/python/ctypes/ctypes_jp.html

アホなので、チュートリアルとリファレンス読んでもイマイチ理解できなかった。
なので、いろいろと使ってみた。

>>> from ctypes import * # これがなくちゃ始まらない。

とりあえず、libcを読み込む。
>>> libc = cdll.LoadLibrary("libc.so.6")
>>> libc

例のアレ。
>>> libc.printf("hello, world\n")
hello, world
13

こんな風に、
LoadLibrary(Library path)
でライブラリが読み込まれた(っていうのかな?)CDLL型オブジェクトが返ってくる。で、
cdll_obj.function_name(args...)
とすると、cdll_objectに読み込まれたライブラリが持つ関数を呼び出し。

int型を作ってみる
>>> ci = c_int(100)
>>> ci
c_long(100)

うちではc_int型とc_long型は同じらしい。

>>> ci = ci+ci
Traceback (most recent call last):
    File "", line 1, in
TypeError: unsupported operand type(s) for +: 'c_long' and 'c_long'

c_int同士の足し算(試してないが、恐らく他の演算も)はできない。

>>> ci+1
Traceback (most recent call last):
    File "", line 1, in
TypeError: unsupported operand type(s) for +: 'c_long' and 'int'

c_intと数字の足し算(試してないが、恐らく他の演算も)もできない。

>>> ci.value
100

c_int_obj.value
で元の中身がみれる。

ということは、
>>> ci = c_int(ci.value + 10)
>>> ci
c_long(110)

で足し算ができる。

ところで、
>>> libc.printf("%d, %d\n", 10, c_int(10))
10, 10
7

c_int使わなくてもできるやん。

c_intじゃないと無理なことを考えてみた。
→c_intじゃないとポインタを持てない?

byref(obj) -> cparam-object
        C コードにおける '&obj' に似ていますが,オブジェクトを C 関数呼び出しのパラメタとして渡すためにしか使えません.
pointer(obj) -> ctypes instance
        C における '&obj' と同じです. 'byref(obj)' とは異なり,'obj' へのポインタとして振舞う新たな ctypes オブジェクトを生成します.

らしい。

>>> byref(10)
Traceback (most recent call last):
    File "", line 1, in
TypeError: byref() argument must be a ctypes instance, not 'int'
>>> pointer(10)
Traceback (most recent call last):
    File "", line 1, in
    File "/usr/lib/python2.5/ctypes/__init__.py", line 310, in pointer
        return POINTER(type(inst))(inst)
    File "/usr/lib/python2.5/ctypes/__init__.py", line 254, in POINTER
        {'_type_': cls})
TypeError: _type_ must have storage info

やっぱり、Pythonの整数型はポインタを持てない

>>> byref(c_int(10))

>>> pointer(c_int(10))

c_int型はポインタを持てる

となると、
>>> t = c_int()
>>> t
c_long(0)
>>> libc.time(byref(t))
1215875875
>>> t
c_long(1215875875)
>>>

お、予想通り。

ところで、文字列。
>>> s="aaa"
>>> libc.strcmp(s, "aaa")
0

よしよし。

Cでは文字列はchar*型だよ(え?const char*だって?今はめんどいので無視)。
>>> libc.strcmp(s, c_char_p("aaa"))
0

ところで、pythonでは文字列型は変更不能だった。
>>> s[1]="b"
Traceback (most recent call last):
    File "", line 1, in
TypeError: 'str' object does not support item assignment

じゃあ、こうするとどうなる?
>>> libc.strcpy(s, "abc")
-1208827276
>>> s
'abc'

すげー。できてる。

調子に乗ってみた。
>>> libc.strcpy(s, "abcde")
-1208827276
>>> s
'abc'

……なんかやっちゃいけないことしちゃったっぽい。

>>> libc.strcpy(s, "z"*1000)
-1208827276
>>> s
セグメンテーション違反です

結論:文字列のデータ自体はいじれる。
けど、強引にメモリ上の値を書き換えているに過ぎない。
(てか、早い話がバッファオーバーフロー?)

配列を知る必要があるような気がする。

ctype * num -> array class
        'ctype' を正の整数で乗算すると, 'Array' のサブクラスを生成します.このサブクラスのインスタンスは 'ctype' のインスタンスを 'num'個保持している配列です

>>> i10 = c_int*10
>>> i10

ここで、i100は配列ではなく、配列を作るクラスであることに注意。

>>> a = i10()
>>> a

中身どーやってみるんだ?

ふつーに考えると。
>>> a[0]
0
>>> a[1]
0

あー、めんどくさい。
>>> a[0:len(a)]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

配列は
・lenが使える
・添字やスライスも使える

↓これもOKだった。
>>> list(a)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> tuple(a)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

当然appendはない
>>> a.append(1)
Traceback (most recent call last):
    File "", line 1, in
AttributeError: 'c_long_Array_10' object has no attribute 'append'

代入する。
>>> a[0]=1
>>> a[1]=c_int(1)
>>> list(a)
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]

普通に数字入れても、c_int型で入れてもどっちでもいいらしい。

型が違うと入れられない。
>>> a[0]=0.1
Traceback (most recent call last):
    File "", line 1, in
TypeError: int expected instead of float instance
>>> a[0]=c_float(0.1)
Traceback (most recent call last):
    File "", line 1, in
TypeError: incompatible types, c_float instance instead of c_long instance

初期値をつけて配列をつくる。
>>> list(i10(1))
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> list(i10(1,2))
[1, 2, 0, 0, 0, 0, 0, 0, 0, 0]

リストやタプルを初期値にしても、思ったようになってくれない
>>> list(i10([1,2]))
Traceback (most recent call last):
    File "", line 1, in
TypeError: int expected instead of list instance
>>> list(i10((1,2)))
Traceback (most recent call last):
    File "", line 1, in
TypeError: int expected instead of tuple instance

forなりなんなりで初期化せなしゃーないのかな。
簡単にする方法が分からなかった。

疲れたので今日はここまで。
これからもやるのかは分からん。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

Stableって古いって意味だっけ? -- Debian初級

読み込み中...