Python与ctypes:为什么printf("%f", 10.1)是错误的

3 投票
2 回答
2024 浏览
提问于 2025-04-17 06:25

我有一个关于Python中ctypes的疑问。

from ctypes import *
printf = cdll.msvcrt.printf
printf("%s\n", "Hello World!")
printf("%d\n", 100)
printf("%f\n", 10.1)

结果:

Hello World!
100
Traceback (most recent call last):
  File "C:\Users\Windows7\Desktop\test.py", line 5, in <module>
    printf("%f\n", 10.1)
ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: Don't know how to convert parameter 2

我知道怎么纠正这个错误:10.1应该用c_double(10.1)来替换,但我想知道为什么在这里要使用c_double()这个函数?前面两个printf根本不需要c_string()c_int()

而且,格式符是"%f",不是"%lf",所以我觉得如果我必须在这里使用ctypes的函数,我应该用c_float()而不是c_double。但是当我尝试printf("%f", c_float(10.1))时,结果却是错误的:0.000000,为什么会这样?

2 个回答

4

根据ctypes的文档

在这些函数调用中,只有None、整数、长整型、字节字符串和Unicode字符串可以直接作为参数使用。None会被当作C语言中的NULL指针传递,字节字符串和Unicode字符串会作为指向它们数据所在内存块的指针传递(char *或wchar_t *)。Python的整数和长整型会被当作平台默认的C语言int类型传递,它们的值会被调整以适应C语言的类型。

所以,你不需要使用c_int或c_string,因为这些都是“原生”的Python对象。

对于你的第二个问题,维基百科上说

f, F:以正常(定点)表示法表示的双精度浮点数。'f'和'F'的区别在于它们如何打印无限大或NaN(不是一个数字)的字符串('f'打印为'inf'、'infinity',而'F'打印为'INF'、'INFINITY'和'NAN')。

MSDN也说,"f"修饰符是用于双精度类型的。

所以,看起来python-ctypes也参考了相同的文档,并认为使用%f时需要提供一个双精度浮点数。

3

试试这个:printf("%f", c_double(10.1))

解释一下:printf() 是一个可以接受可变参数的函数。格式参数告诉你在堆栈上可以期待什么类型的数据。

问题是:在查看格式时,无法区分 floatdouble 这两种参数,但 float 占用 4 个字节,而 double 占用 8 个字节。那么 printf() 怎么知道代码中推送到堆栈上的到底是哪种呢?

答案是:float 类型的数据总是会被转换成 double。这样一来,所有浮点类型在堆栈上使用的字节数都是一样的,printf() 就能搞清楚它们的地址了。

撰写回答