printf 和 size_t 使用 FFI

Sté*_*ent 4 c haskell ffi

为了size_t在 C 中用打印整数printf,转换格式器是%zu

但是,当我使用printfwith 时%zu,通过 FFI 打印zu而不是整数调用 Haskell 中的 C 函数。如何解决?

最小的例子

文件zu.c

#include <stdio.h>

void printzu(){
    size_t x = 666;
    printf("x=%zu", x);
}
Run Code Online (Sandbox Code Playgroud)

模块库.hs

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

foreign import ccall unsafe "printzu" printzu' :: IO ()
Run Code Online (Sandbox Code Playgroud)

测试

Prelude> import Lib
Prelude Lib> printzu'
x=zu
Run Code Online (Sandbox Code Playgroud)

小智 5

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

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

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

size_t x = 666;
printf("x=%lu", (unsigned long)x);
Run Code Online (Sandbox Code Playgroud)

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

  • 该平台实际上有较大的size_tunsigned long(这是真的适用于带,不幸的是,Win64的LLP64数据模型64位系统)和
  • 您在运行时确实有一个不适合unsigned long. 这必须至少大于 4G (2 32 ) 的值,因为这是unsigned long.

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


如果问题特别出在 MSVCRT.DLL 上,并且您想总体上坚持使用 C99 或更高版本,我建议使用inttypes.h较早的答案中的方法。这永远不会在 Windows 上打印错误的值(并且在其他平台上仍然需要符合 C99 的标准库)。