c - 如何打印未知大小的类型,如 ino_t?

  显示原文与译文双语对照的内容
0 0

我经常会遇到以下情况:我想打印和 printf整数类型的实现定义大小的值( 如 ino_ttime_t) 。 现在,我使用这样的模式对于此:

#include <inttypes.h>
ino_t ino; /* variable of unknown size */
printf("%" PRIuMAX, (uintmax_t)ino);

到目前为止这种方法是有效的,但它也有几个缺点:

  • 我要知道我在打印类型是否带符号或无符号。
  • 我不得不用类型转换,可能放大我的代码。

那有没有更好策略?

时间:原作者:9个回答

0 0
#include <inttypes.h>
ino_t ino; /* variable of unknown size */
/* ... */
printf("%" PRIuMAX, (uintmax_t)ino);

那一定会工作( 有一些限制性条款,请参见以下内容),只是命令格式为:

printf("%ju", (uintmax_t)ino);

j长度修饰符

指定按以下 diouxx转换指定符适用于某个 intmax_tuintmax_t后按键或参数,n转换指示符,该指针指向一个适用于 intmax_t参数。

还有一些 zt乘数。 size_tptrdiff_t( 及其相应的分别为50.50 signed/unsigned类型) 。

就我个人而言我觉得这格式字符串中定义宏 <inttypes.h>难看,难以记忆,这就是我把 "%ju""%jd".

正如你提到的,有必要分辨不出类型( ino_t在这种情况下) 是有符号或无符号的。 如果你碰巧不知道那个,是可以想办法吧:

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#define IS_SIGNED(type) ((type)-1 < (type)0)
#define DECIMAL_FORMAT(type) (IS_SIGNED(type) ? "%jd" : "%ju")
#define CONVERT_TO_MAX(type, value) 
    (IS_SIGNED(type) ? (intmax_t)(value) : (uintmax_t)(value))
#define PRINT_VALUE(type, value) 
    (printf(DECIMAL_FORMAT(type), CONVERT_TO_MAX(type, (value))))
int main(void) {
    ino_t ino = 42;
    PRINT_VALUE(ino_t, ino);
    putchar('n');
}

虽然这部分可能是过度杀戮 如果确实短于64位,则可以将该值转换为的类型。 intmax_t,则该值将被保留。 也可以使用 uintmax_t打印所有值,但其结果是定义完善的。 -1作为 18446744073709551615( 2 64 1 ) 可能有点迷惑。

所有的这些工作只有在C 允许重复的值。 <stdint.h>j长度修饰符。 printf我如果不包含了,如果它支持C99 。 并非所有的编译器都这样做( 咳嗽 microsoft 咳嗽 ) 。 对于C90,最宽的整型, longunsigned long,也可以把这些和使用 "%ld"和/ 或 "%lu". 理论上可以测试是否符合C99使用 __STDC_VERSION__宽度超过预定义的宏虽然有些pre C99编译器仍支持类型 longunsigned long作为一个扩展。

原作者:
0 0

这里的" 大小" 整数类型无关,但其值范围内。

以你显然双方,他们好像是可以强制转换为 uintmax_tintmax_t轻松解决任何二义性,则在 printf()呼叫。

有符号或无符号类型的问题可以用简单的方法解决:

  • 所有无符号整数操作正常运行模" n " 对某些正值N,具体的类型。 这意味着每个结果只涉及无符号整数类型提供一个负值。
  • 要检测一个变量 x将为有符号或无符号类型,它足以验证是否 x-x都是非负的值。

例如:

 if ( (x>=0) && (-x>=0) )
    printf("x has unsigned type");
 else
    printf("x has signed type");

现在,我们可以编写一些宏:

( Edited: 名称和表达式的宏发生改变)

 #include <inttypes.h>
 #include <limits.h>
 #define fits_unsigned_type(N) ( (N >= 0) && (  (-(N) >= 0) || ((N) <= INT_MAX) ) )
 #define smartinteger_printf(N) 
     (fits_unsigned_type(N)? printf("%ju",(uintmax_t)(N)): printf("%jd",(intmax_t) (N)) )
// ....
ino_t x = -3;
printf("The value is: "); 
smartinteger_printf(x);
//.....

Note : 有符号或无符号字符变量是否是欠佳时,被上面的宏的值为0 。 但在这种情况下一切运转良好,因为0 中具有相同的位表示有符号或无符号类型。

第一个宏可以用来检测是否基础类型的数学对象具有unsgined类型或不。
此结果是在第二个宏用来选择的方式打印在屏幕上的对象。

第一REEDITION :

  • 作为 Pascal Cuoq 指出在他的评论中,整数促销不得不采取accont ( 对于unsigned charshort值fiiting范围内的 int. 这相当于问如果值为在雷哥0 到 INT_MAX.

所以我改变了的宏名, fits_signed_type.
另外,我修改了宏快合法的正 int值。

fits_unsigned_type可以判断一个对象是否有无符号整数类型,在大多数情况下。

  • 如果该值为负,显然类型不 unsigned.
  • 如果该值N 为正然后
    • 如果 -N为正,那么N 就 unsignedtype,
    • 如果 -N为负,但N 的范围为0 到 INT_MAX,则类型的 N可能是 signedunsigned,但它会融合在正值的范围的 int符合说法范围内的 uintmax_t.

第二REEDITION :

ir看来有这里方法可以解决同样的问题。 我的方法需要在帐户数值区域, 整数提升规则来生成正确的打印值与 printf(). 另一方面,格雷戈尔Szpetkowski能够为确定带符号的字符类型以直线形式。 我喜欢这两个。

原作者:
0 0

自从你已经使用C99标头,则有机会使用精确宽度格式说明符取决于 sizeof(T)和signed/unsigned检查。 但是必须这样做预处理阶段之后( 因此遗憾 ##不能使用运算符来构造PRI令牌) 。 我有个建议:

#include <inttypes.h>
#define IS_SIGNED(T) (((T)-1) < 0) /* determines if integer type is signed */
...
const char *fs = NULL;
size_t bytes = sizeof(T);
if (IS_SIGNED(T))
    switch (bytes) {
        case 1: fs = PRId8;  break;
        case 2: fs = PRId16; break;
        case 4: fs = PRId32; break;
        case 8: fs = PRId64; break;
    }
else
    switch (bytes) {
        case 1: fs = PRIu8;  break;
        case 2: fs = PRIu16; break;
        case 4: fs = PRIu32; break;
        case 8: fs = PRIu64; break;
    }

使用此方法不需要强制转换了,但是格式字符串必须手动构造再将其传递到 printf( 我如果不包含。 没有自动字符串连接) 。 下面是一些可运行的示例:

#include <stdio.h>
#include <inttypes.h>
#define IS_SIGNED(T) (((T)-1) < 0)
/* using GCC extension: Statement Expr */
#define FMT_CREATE(T) ({                      
    const char *fs = NULL;                    
    size_t bytes = sizeof(ino_t);             
    if (IS_SIGNED(T))                         
        switch (bytes) {                      
            case 1: fs = "%" PRId8;  break;   
            case 2: fs = "%" PRId16; break;   
            case 4: fs = "%" PRId32; break;   
            case 8: fs = "%" PRId64; break;   
        }                                     
    else                                      
        switch (bytes) {                      
            case 1: fs = "%" PRIu8;  break;   
            case 2: fs = "%" PRIu16; break;   
            case 4: fs = "%" PRIu32; break;   
            case 8: fs = "%" PRIu64; break;   
        }                                     
    fs;                                       
})
int main(void) {
    ino_t ino = 32;
    printf(FMT_CREATE(ino_t), ino); putchar('n');
    return 0;
}

注意这需要某些小诡计的 Statement Expr,但可能存在一些其他方式( 这是" 价格" 也是泛型的) 。

编辑:

这是第二版,不需要特定的编译器扩展( 别担心我不能读也行啊) 这样使用函数宏:

#include <stdio.h>
#include <inttypes.h>
#define IS_SIGNED(T) (((T)-1) < 0)
#define S(T) (sizeof(T))
#define FMT_CREATE(T)   
    (IS_SIGNED(T)        
        ? (S(T)==1?"%"PRId8:S(T)==2?"%"PRId16:S(T)==4?"%"PRId32:"%"PRId64) 
        : (S(T)==1?"%"PRIu8:S(T)==2?"%"PRIu16:S(T)==4?"%"PRIu32:"%"PRIu64))
int main(void)
{
    ino_t ino = 32;
    printf(FMT_CREATE(ino_t), ino);
    putchar('n');
    return 0;
}

请注意,条件运算符뿪 assiociativity ( 因此它的演算值为从左到右按预期) 。

原作者:
0 0

使用C11类型泛型宏,有可能在编译时构造格式字符串,请.g .:

#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#define PRI3(B,X,A) _Generic((X), 
                             unsigned char: B"%hhu"A, 
                             unsigned short: B"%hu"A, 
                             unsigned int: B"%u"A, 
                             unsigned long: B"%lu"A, 
                             unsigned long long: B"%llu"A, 
                             signed char: B"%hhd"A, 
                             short: B"%hd"A, 
                             int: B"%d"A, 
                             long: B"%ld"A, 
                             long long: B"%lld"A)
#define PRI(X) PRI3("",(X),"")
#define PRIFMT(B,X,A) PRI3(B,(X),A),(X)
int main () {
    signed char sc = SCHAR_MIN;
    unsigned char uc = UCHAR_MAX;
    short ss = SHRT_MIN;
    unsigned short us = USHRT_MAX;
    int si = INT_MIN;
    unsigned ui = UINT_MAX;
    long sl = LONG_MIN;
    unsigned long ul = ULONG_MAX;
    long long sll = LLONG_MIN;
    unsigned long long ull = ULLONG_MAX;
    size_t z = SIZE_MAX;
    intmax_t sj = INTMAX_MIN;
    uintmax_t uj = UINTMAX_MAX;
    (void) printf(PRIFMT("signed char       : ", sc, "n"));
    (void) printf(PRIFMT("unsigned char     : ", uc, "n"));
    (void) printf(PRIFMT("short             : ", ss, "n"));
    (void) printf(PRIFMT("unsigned short    : ", us, "n"));
    (void) printf(PRIFMT("int               : ", si, "n"));
    (void) printf(PRIFMT("unsigned int      : ", ui, "n"));
    (void) printf(PRIFMT("long              : ", sl, "n"));
    (void) printf(PRIFMT("unsigned long     : ", ul, "n"));
    (void) printf(PRIFMT("long long         : ", sll, "n"));
    (void) printf(PRIFMT("unsigned long long: ", ull, "n"));
    (void) printf(PRIFMT("size_t            : ", z, "n"));
    (void) printf(PRIFMT("intmax_t          : ", sj, "n"));
    (void) printf(PRIFMT("uintmax_t         : ", uj, "n"));
}

有一个潜在的问题,但是: 如果有类型不同于所列之外( 我如果不包含了, signedunsigned版本的 char, short, int, long以及 long long),这不适用于这些类型。 此外,也不能添加类型如 size_tintmax_t对该类型泛型宏" 万一",因为它将导致一个错误如果他们typedefd 从列出的已经类型之一。 我离开了 default如果未指定时,使该宏生成一个编译时错误没有找到匹配的类型。

但是,如示例程序所示, size_tintmax_t平台上很好地工作在哪里一样列出的类型之一相同( e .g 。, long) 。 同样没有问题if,e .g 。 longlong longlongint,类型相同。 但更安全的版本可能只是强制转换为 intmax_tuintmax_t根据signedness ( 如其他答案所示),并创建一个类型泛型宏有那样options…

示意问题是通用类型宏扩展用括号括起来的字符串,以防止连接与相邻文本字符串以常规的方式 这样可以防止如下信息:

(void) printf("var = " PRI(var) "n", var); // does not work!

这就有了这个 PRIFMT这一常见的情况下打印单个变量的前缀和后缀的宏,包括:

(void) printf(PRIFMT("var = ", var, "n"));

( 注意,这样做就容易展开此支持的非整数类型的宏, printf,e .g 。 double, char *… )

原作者:
...