c - 使用 FFI 的带有 size_t 的 printf

标签 c haskell ffi

为了在 C 语言中使用 printf 打印一个 size_t 整数,转换格式化程序是 %zu

然而,当我将 printf%zu 一起使用时,通过 FFI 调用 Haskell 中的 C 函数会打印 zu 而不是整数。如何解决?

最小示例

文件 zu.c

#include <stdio.h>

void printzu(){
    size_t x = 666;
    printf("x=%zu", x);
}

模块 Lib.hs

{-# LANGUAGE ForeignFunctionInterface #-}
module Lib
  where
import Foreign

foreign import ccall unsafe "printzu" printzu' :: IO ()

测试

Prelude> import Lib
Prelude Lib> printzu'
x=zu

最佳答案

由于 printf() 是 C 标准库的一部分,它通常在某些运行时库中实现。当它被动态链接时,如果根据哪个进程调用代码,链接了不同版本的库,则可以使用相同的代码产生这样的效果。如果 %zu 不工作,它是一个不支持 C99 的旧版本。

在 Windows 上,它很可能是系统的 MSVCRT.DLL,它不再供公众使用,但与旧的 MS Visual C 6 版本保持兼容。例如 MinGW 默认链接到那个库,所以你不需要发布你自己的 C 运行时。这当然有将库函数限制为 C89/C90 的缺点。

打印 size_t 的一个通常相当安全的做法是将其转换为 unsigned long 并打印:

size_t x = 666;
printf("x=%lu", (unsigned long)x);

这只会给出错误的结果,如果

  • 该平台实际上有一个比 unsigned long 更大的 size_t(这是真的,例如对于具有 LLP64 数据模型的 64 位系统,不幸的是,win64)和
  • 您在运行时确实有一个不适合 unsigned long 的大小。该值必须至少大于 4G (232),因为这是 unsigned long 的保证最小范围。

请注意 Actor 阵容在这里非常重要。因为 printf() 是一个可变参数函数,原型(prototype)看起来就像 printf(const char *fmt, ...),所以编译器没有可用的类型信息 - - 因此无法进行自动转换。


如果问题具体出在 MSVCRT.DLL 上,而您一般想坚持使用 C99 或更高版本,I suggested a method using inttypes.h in an earlier answer .这将永远不会在 Windows 上打印错误的值(并且在其他平台上仍然需要符合 C99 的标准库)。

关于c - 使用 FFI 的带有 size_t 的 printf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49942879/

相关文章:

haskell - 我很困惑为什么这种合并排序的实现不起作用?

haskell - 应用仿函数 : why can fmap take a function with more than one argument?

haskell - 在 Haskell 中解开结的任何好工具?

haskell - 从 Haskell 访问原始 argv 指针

haskell - 对 Haskell FFI 的反馈

javascript - 如何将 PureScript ADT 转换为 FFI 的 JS 字符串 'enum'

c - Malloc 用于 void 指针数组的单个元素

c - 硬件/软件实现

c - 如何在c中读取图像的像素?

python - 使用 ctypes : passing vectors 从 python 调用 c