【问题标题】:Python not passing correct size of string to CPython没有将正确大小的字符串传递给C
【发布时间】:2021-01-29 15:04:12
【问题描述】:

当我尝试将 16 个字符的字符串从 python 传递到 C 并对其进行加扰时,我不断收到随机错误代码。

s = ctypes.c_wchar_p("H86ETJJJJHGFTYHr")


print(libc.hash_password(s))

在代码的开头我添加了一条语句来将字符串的大小返回给python, 但是它一直返回值 8

if (sizeof(my_string) != 17) return sizeof(my_string);

如果我尝试返回数组的单个元素,它将返回一个数字,我假设这是字符的 ascii 值,并且代码不会出错。

这也适用于最后一个元素,它被正确识别为 null。

代码在 C 本身中完美运行。那么如何让 C 接受正确大小的字符串,或者让 python 接受返回字符串呢?

编辑:忘了提,当我这样做时

sizeof(*my_string)

它返回一个 1

编辑 2: 这是函数定义

unsigned char *hash_password(char *input_string)

【问题讨论】:

  • 17 是字符串的长度。 char* 类型的变量在 64Bit 上的大小是 8 字节,这就是 sizeof 返回的。
  • 我知道字符串的大小是 17,这就是为什么如果它不是 17,我会放置早期的 return 语句。好吧,但我不知道为什么要这样做 sizeof(*my_string)返回 1。
  • @PythonProgrammer 在 C 中编码时,你不会戴着 Python 眼镜完成它。
  • @PythonProgrammer sizeof operator, char, wchar_t etc
  • mystringchar**mystring 指的是指针处的 单个 charchar 的大小为 1。使用strlen 计算从指针开始直到找到的第一个空字节的所有字符。

标签: python c ctypes


【解决方案1】:

在 Python 3 中,"H86ETJJJJHGFTYHr" 是由 Unicode 代码点组成的 str 对象。你的 C 函数声明是unsigned char *hash_password(char *input_string)。当通过ctypes 而不是char* 传递时,Python str 被编组为wchar_t*。为此使用bytes 对象。

假设sizeofctypes.sizeof,它像C 一样工作并返回等效C 对象的大小。对于c_wchar_p,即w_char_t*,指针的大小通常为 4 或 8 字节(32 位或 64 位操作系统)。不是字符串的长度。

总是在使用ctypes 时声明函数的参数类型和返回类型也是一个好主意,这样它就可以正确检查参数的类型和数量,而不是猜测:

import ctypes

dll = ctypes.CDLL('./your.dll')
dll.hash_password.argtypes = ctypes.c_char_p,
dll.hash_password.restype = ctypes.c_char_p

一个简单粗暴的例子(注意printf 返回打印字符串的长度):

>>> from ctypes import *
>>> dll = CDLL('msvcrt')
>>> dll.printf('hello\n')  # ctypes assume wchar_t* for str, so passes UTF16-encoded data
h1                         # of 68 00 65 00 ... and prints only to first null, 1 char.
>>> dll.printf.argtypes=c_char_p, # tell ctypes the correct argument type
>>> dll.printf('hello\n')           # now it detects str is incorrect.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
>>> dll.printf(b'hello\n')          # pass bytes, and `char*` is marshaled to C
hello
6

【讨论】:

  • 哇,谢谢,C 现在没有返回任何错误!但是您能否澄清一下到底发生了什么,例如,C 现在是接收 Unicode 字符,还是它们在 ascii 中?当我将字符串从 C 返回到 python 时,它工作得很好,但是当我对字符执行算术时,我得到诸如 b'}1&t\xb6`\xf6\...' 之类的东西
  • @PythonProgrammer 一个字节串包含字节。 ASCII 只是为了显示方便。 h 是代表字节值 0x68 的 ASCII 字符,但字节是基础数据。 \xb6 在您的示例中只是字节值 0xB6 的显示表示形式。
【解决方案2】:

在 C 中,sizeof 永远不会返回字符串的长度,而是返回变量在内存中的大小。

对于声明为的字符串

char *string;

那么 string 是一个指向字符的指针,在您的系统上,指针似乎是 64 位(即 8 位)。

当您在 C 中执行 *string 时,您将获得 string 指向的第一个元素的内容 - 即单个字符。

要在 C 中获取字符串的长度,请使用 strlen(my_string)

【讨论】:

  • 是的,它以字节为单位返回大小,但我希望看到大小为 17,因为每个字符都是 1 个字节,对吗?哦,我现在看到了问题......谢谢,我只传递了第一个字符,但我需要 **my_string 来传递整个事情,对吗?
  • **mystring 只有在 *my_string 是指针时才有效。
【解决方案3】:

sizeof 返回内存中对象的大小。这和字符串的长度不是一回事。

在您的 C 代码中,my_string 是一个指向字符串的指针sizeof(my_string) 是这个指针的大小:在 64 位机器上是 8 个字节。 sizeof(*my_string)my_string 指向的大小。由于您得到 1,这可能意味着您的 C 代码中存在另一个问题,即您将单字节字符(char,根据定义其大小始终为 1)和宽字符(@987654328 @,其大小几乎总是 2 或 4)。

您的字符串是一个以空字符结尾的宽字符串。要获取它在 C 中的长度,请调用 wcslen。请注意,这意味着您的整个字符串处理代码必须使用wchar_twcsxxx 函数。如果您的字符串是字节字符串,请使用charstrlen 和其他适用于字节字符串的函数。

【讨论】:

  • 其他适用于字节字符串的函数——你的意思是在 Python 中? (我在问c_wchar_p 是否总是正确的。)
  • 老实说我不知道​​,我会把我的函数定义放在一个编辑中,这样你就可以看到我传递了什么。
  • @Wolf 不,在 C 中。当您使用 c_char_p 时,ctypes 模块将正确地将 Python 字符串转换为 char 数组,并在您使用时转换为 wchar 数组c_wchar_p。至少如果您正确设置了语言环境和编码(我不知道该怎么做)。
  • @Gilles'SO-stopbeingevil' c_wchar_p vs char* - 这正是我现在在问题中看到的二分法;)
猜你喜欢
  • 1970-01-01
  • 2021-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
相关资源
最近更新 更多