引言

先说结论:
python3中的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节(最常用的是用两个字节表示一个字符,如果要用到非常偏僻的字符,就需要4个字节)。
如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python3对bytes类型的数据用加b前缀表示:x = b’ABC’

而python2中的普通字符串’ABC’是ASCII编码的,用加前缀u’ABC’来表示是Unicode编码

现象

尝试在Linux下用python调用C的动态库(so) ,发现python中字符串传入到.so中与预期结果不一致。
环境,python3.6+gcc
a.c代码如下

#include <stdio.h>

void show(char* msg) {
    printf("the msg is:%s\n",msg);
}

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

master.py代码如下

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from ctypes import *

cur = cdll.LoadLibrary('./a.so')

msg='Hello,Calling OK'

cur.show(msg)

print(cur.add(1, 20))

然后执行

gcc a.c -fPIC -shared -o a.so

在当前目录下会产生一个a.so文件

其中

-fPIC是position independent code(位置无关代码)的意思

-shared是产生一个可以与其他对象连接来形成一个可执行文件的共享对象的一个参数

然后执行

$ python master.py 
Hello,Calling OK
the msg is:H    //而非Hello,Calling OK
21

发现调用so中的show后,%s只输出了H

将代码改为

msg=b'Hello,Calling OK'

结果输出正常,为the msg is:Hello,Calling OK

分析

因为python3中的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节(最常用的是用两个字节表示一个字符,如果要用到非常偏僻的字符,就需要4个字节)。
如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python3对bytes类型的数据用加b前缀表示:x = b’ABC’

所以以上输出不正确的原因是unicode的两字节与Char的一字节不匹配,只有加前缀b的字符串才能正确的传给.so文件中的char* msg

而python2中的普通字符串’ABC’是ASCII编码的,用加前缀u’ABC’来表示是Unicode编码
调试发现上述代码msg=’Hello,Calling OK’在python2下运行正常,而msg=u’Hello,Calling OK’只输出了H,与预期一致

进一步查看python2.7的ctypes手册可见如下类型对应关系

python2和python3字符串编码 python2 字符串_ico


所以python2.7下msg=c_char_p(‘Hello,Calling OK’),而msg=c_wchar_p(‘Hello,Calling OK’)只输出H

参考

聊聊Python ctypes 模块
浅谈python中使用C/C++:ctypes
字符串和编码