簡體   English   中英

使用FFI的size_t的printf

[英]printf with a size_t using the FFI

要使用printf在C中打印size_t整數,轉換格式化程序為%zu

但是當我使用帶有%zu printf時,通過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(2 32 )的值,因為這是unsigned long的保證最小范圍。

請注意,演員陣容在這里非常重要。 因為printf()是一個可變函數,所以原型看起來像printf(const char *fmt, ...) ,所以沒有可用的編譯器的類型信息 - 因此無法進行自動轉換。


如果問題是MSVCRT.DLL,並且你想要堅持使用C99或更高版本, 我建議在早期的答案中使用inttypes.h的方法 永遠不會在Windows上打印錯誤的值(並且仍然需要在其他平台上使用符合C99的標准庫)。

當沒有實現"%zu" ,替代方案是轉換為某種寬類型並打印,具有適度的截斷風險。

size_t sz = foo();
printf("%lu\n", (unsigned long) sz);  // risk of truncation.

代碼可以嘗試其他整數寬類型,如uintmax_tunsigned long long ,但如果沒有實現"%zu" ,則可能還沒有實現"%ju""%llu"

通過部分打印可以避免截斷。

printf("%lX%08lX\n", 
    (unsigned long) (sz/0x10000u/0x10000u), (unsigned long) (sz & 0xFFFFFFFFu));

// remote truncation risk remains.
printf("%lu%09lu\n", 
    (unsigned long) (sz/1000000000u), (unsigned long) (sz%1000000000u));

可以使用更復雜的代碼來避免前導數字。

我想提供另一種方法來處理不符合C99 / C11標准但仍提供64位或更寬類型的系統。

導入並包含一個stdint.h/inttypes.h旨在將舊系統橋接到新的C99標准。

示例: C99 stdint.h標頭和MS Visual Studio

然后施放到他們可用的廣泛類型

#if SIZE_MAX > ULONG_MAX
// Include from the standard location or wherever the imported included files are saved.
#include <stdint.h>
#include <inttypes.h>

void printzu(){
    size_t x = 666;
    printf("x=%" PRIuMAX "\n", (uint_max_t) x);
}

#else
void printzu(){
    size_t x = 666;
    printf("x=%lu\n", (unsigned long) x);
}
#endif

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM