现在的位置: 首页 > 综合 > 正文

python调用c

2013年07月23日 ⁄ 综合 ⁄ 共 3074字 ⁄ 字号 评论关闭

python一个非常的大的优点就是开发效率高,非常不好的缺点就是执行效率低;然而c语言有个则刚好相反。还有一点python的对源码的保护做不到,即使你用py2exe,pyinstaller这样的方法也是很容易被反编译出来。但是c写的代码反编译的难度就极大地增加。所以如果你写的代码里面如果包含了一些敏感的东西,那么你可以把这段代码使用c来写。

那么我们是不是可以结合起来使用呢?

答案是完全可以,用c来写一个动态库/共享库,然后在python中调用。

python 的解释器本身就是用c写的,所以调用c是很方便的,调用的方法不止一种,我们这里介绍使用ctypes这种方式。

我们会写一个测试代码来演示怎么在python中调用c函数,包括python传整型和字符串参数给c库,c库中返回整型和字符串。这个测试将在windows xp平台+python2.7实施。

接下来我们把要做的事情分成2个阶段,用c编写一个动态库,在python中调用这个动态库。

c编写一个动态库

使用vc6创建一个空的win32动态库,添加2个文件

testcdll.c

tsetcdll.def

testcdll.c的内容如下:

#include <stdlib.h>
#include <string.h>


int add( int a, int b )
{
	return a + b;
}


char* pStr = 0;
int nMax = 128;
char* makestring( )
{
	pStr = ( char* )malloc(nMax);
	memset( pStr, 0, nMax );
	strcpy( pStr, "test is test string" );
	
	return pStr;
}

void delstring( void )
{
	if ( 0 != pStr )
	{
		free( pStr );
		pStr = 0;
	}
}

char* catstring( char* pinput )
{
	if ( 0 == pinput )
	{
		return 0;
	}
	if ( 0 == pStr )
	{
		pStr = ( char* )malloc(nMax);
	}
	memset( pStr, 0, nMax );
	strcpy( pStr, "test is test string--" );
	strcat( pStr, pinput );
	return pStr;
}

然后编译,得到后面测试要用到的testcdll.dll

如果你不想用IDE的话, 你可以直接使用命令行来编译:

cl /LD testcdll.c tsetcdll.def  /link /out:testcdll.dll

调用这个动态库

因为我们的测试比较简单所以我们就不写源码文件,直接在解释器中写测试代码了。

打开命令行cmd,cd到刚才我们生成dll的文件夹,执行python

到解释器界面,依次执行下面的命令:

>>> from ctypes import *
>>> hd = cdll.LoadLibrary('FoxLicenseMgr.dll')
>>> hd.add( 2, 3 )
5
>>> hd.makestring.restype = c_char_p
>>> hd.makestring()
'test is test string'
>>> hd.delstring()
1
>>> s1 = 'python'
>>> cs1 = c_char_p( s1 )
>>> hd.catstring(cs1)
12068640
>>> hd.catstring.restype = c_char_p
>>> hd.catstring(cs1)
'test is test string--python'
>>> dir(hd)
['_FuncPtr', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '_
_getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__modul
e__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__si
zeof__', '__str__', '__subclasshook__', '__weakref__', '_func_flags_', '_func_re
stype_', '_handle', '_name', 'catstring', 'delstring', 'makestring']
>>> exit()

对上面语句稍作解释,

from ctypes import *

因为我们使用的是ctypes的这种方式,需要导入ctypes。

hd = cdll.LoadLibrary('testcdll.dll')

加载我们之前生成的动态库,因为我们已经cd到testcdll.dll所在的目录所以不用加全路径,否则你需要写全路径的。

hd.add( 2, 3 )

直接调用add函数,传进2个参数,参数不需要额外的转换,直接传进去,有些类型是不可以直接传的,需要做个转换,这个后面会提到。调用这一句后,就直接输入结果到控制台上结果为5

调用返回为字符串的函数的时候,需要设置返回类型,否则就会返回一整型值,这是python调用c函数的默认的返回值,如上面调用catstring的之前没有设置返回值,结果返回一个数值

>>> hd.catstring(cs1)
12068640

设置返回类型使用下面的语法:

hd.makestring.restype = c_char_p

因为我们的返回类型是char*, 所以使用c_char_p,这个和char*是对应的。其他的python的ctypes的类型和c类型的映射关系如下:

ctypes type C type Python type
c_bool _Bool bool (1)
c_char char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte unsigned
char
int/long
c_short short int/long
c_ushort unsigned
short
int/long
c_int int int/long
c_uint unsigned
int
int/long
c_long long int/long
c_ulong unsigned
long
int/long
c_longlong __int64 orlonglong int/long
c_ulonglong unsigned
__int64
orunsignedlonglong
int/long
c_float float float
c_double double float
c_longdouble long
double
float
c_char_p char
*
(NUL terminated)
string or None
c_wchar_p wchar_t
*
(NUL terminated)
unicode or None
c_void_p void
*
int/long or None

调用makestring()来返回一个字符串:

>>> hd.makestring()
'test is test string'

如果传入的是字符串,也需要做一个转换

>>> s1 = 'python'
>>> cs1 = c_char_p( s1 )

然后再传入,因为这个函数的返回的是字符串因此也需要设置返回类型:

>>> hd.catstring.restype = c_char_p
>>> hd.catstring(cs1)
'test is test string--python'

最后使用dir来查看这个hd的都有那些可用的方法。

完。

抱歉!评论已关闭.