如何打印像ino_t这样的未知大小的类型?

fuz*_*fuz 23 c printf portability

我经常会遇到我想要printf使用整数类型的实现定义大小(如ino_ttime_t)的值进行打印的情况.现在,我使用这样的模式:

#include <inttypes.h>

ino_t ino; /* variable of unknown size */
printf("%" PRIuMAX, (uintmax_t)ino);
Run Code Online (Sandbox Code Playgroud)

这种方法到目前为止有效,但它有一些缺点:

  • 我必须知道我正在尝试打印的类型是签名还是未签名.
  • 我必须使用可能会扩大我的代码的类型转换.

有更好的策略吗?

Kei*_*son 9

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

这肯定会有效(有一些附带条件;见下文),但我会用:

printf("%ju", (uintmax_t)ino);
Run Code Online (Sandbox Code Playgroud)

j长度修改

用于指定后续的d,i,o,u, x,或X转换说明适用于一个intmax_tuintmax_t参数; 或者以下n转换说明符适用于指向intmax_t参数的指针.

也有zt改性剂size_tptrdiff_t(和它们对应的符号/无符号类型),分别.

而我个人觉得在定义的格式字符串宏<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');
}
Run Code Online (Sandbox Code Playgroud)

虽然这可能有点矫枉过正.如果您确定类型的值小于64位,则可以将值转换为intmax_t,并保留该值.或者您可以使用uintmax_t并获得所有值的明确定义的结果,但打印-118446744073709551615(2 64 -1)可能有点令人困惑.

所有这些只有在您的C实现支持<stdint.h>j长度修饰符时才有效printf- 即,如果它支持C99.并非所有编译器都这样做(咳嗽微软咳嗽).对于C90,最宽的整数类型是longunsigned long,您可以转换为那些并使用"%ld"和/或"%lu".理论上你可以测试使用C99遵守__STDC_VERSION__预定义宏-尽管一些C99的预编译器可能仍然支持类型比更宽long,并unsigned long作为扩展.


Ark*_*kku 7

使用C11类型的通用宏,可以在编译时构造格式字符串,例如:

#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"));
}
Run Code Online (Sandbox Code Playgroud)

还有一个潜在的问题,虽然:如果有类型与列出的不同(即比其他signedunsigned版本charshortintlong,和long long),这是行不通的那些类型。它也不是可以添加类型,如size_tintmax_t为“以防万一”式的通用宏,因为如果他们会导致错误 typedef从已经上市的类型之一天。我没有default指定大小写,以便在找不到匹配类型时宏会产生编译时错误。

然而,如在示例程序,size_t以及intmax_t工作只是罚款,他们是一样的上市类型(例如,同为一个平台long)。同样,如果long和和long longlongint是相同类型,也没有问题。但是更安全的版本可能只是强制转换为签名intmax_tuintmax_t根据签名进行签名(如其他答案所示),并仅使用这些选项来创建类型通用宏…

一个外观上的问题是,泛型类型的宏在字符串文字周围使用括号进行扩展,从而以通常的方式阻止了与相邻字符串文字的串联。这样可以防止出现以下情况:

(void) printf("var = " PRI(var) "\n", var); // does not work!
Run Code Online (Sandbox Code Playgroud)

因此,PRIFMT对于打印单个变量的常见情况,包含带有前缀和后缀的宏:

(void) printf(PRIFMT("var = ", var, "\n"));
Run Code Online (Sandbox Code Playgroud)

(请注意printf,使用doublechar *… 支持的非整数类型扩展此宏非常简单)


pab*_*977 6

整数类型的"大小"在这里不相关,但是它的值范围.

显然你已经尝试过了,可以转换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");
Run Code Online (Sandbox Code Playgroud)

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

(已编辑:宏的名称和表达已更改)

 #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);
//.....
Run Code Online (Sandbox Code Playgroud)

注意:当值为0时,上面的宏没有很好地检测到变量的有符号或无符号字符.但是在这种情况下一切都运行良好,因为0在有符号或无符号类型中具有相同的位表示.

第一个宏可用于检测算术对象的基础类型是否具有未编号类型.
此结果在第二个宏中用于选择在屏幕上打印对象的方式.

第一次罚款:

  • 正如Pascal Cuoq在他的评论中指出的那样,整数促销必须采用accont中的无符号charshort值范围内的值int.这相当于询问值是否在rango 0到INT_MAX.

所以我已经将宏的名称更改为fits_signed_type.
此外,我已修改宏以考虑正值int.

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

  • 如果值为负,显然类型不是unsigned.
  • 如果值N是正的那么
    • 如果-N是正数,那么N有unsigned类型,
    • 如果-N是负数,但是N在0到0的范围内INT_MAX,那么类型N可以是signedunsigned,但是它将适合于正值的范围int,其适合范围uintmax_t.

第二次罚款:

Ir似乎有解决同样问题的方法.我的方法考虑了值范围和整数提升规则,以产生正确的打印值printf().另一方面,Grzegorz Szpetkowski的方法确定了直线形式的有符号字符.我喜欢这两个.

  • 您可以通过((1?-1:(expr))&lt;0)来确定表达式的有序性(至少具有“ int”级)*而无需对其进行评估*。 (2认同)