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

时间:2021-07-21 05:15:35

I often experience situations where I want to print with printf the value of an integer type of implementation-defined size (like ino_t or time_t). Right now, I use a pattern like this for this:

我经常遇到需要用printf打印实现定义大小的整数类型的值(如ino_t或time_t)的情况。现在,我用这样的模式来做这个:

#include <inttypes.h>

ino_t ino; /* variable of unknown size */
printf("%" PRIuMAX, (uintmax_t)ino);

This approach works so far but it has a couple of disadvantages:

这种方法到目前为止还有效,但它有两个缺点:

  • I have to know whether the type I'm trying print is signed or unsigned.
  • 我必须知道我正在尝试打印的类型是有符号的还是无符号的。
  • I have to use a type cast that possibly enlarges my code.
  • 我必须使用类型强制转换,可能会放大我的代码。

Is there a better strategy?

有更好的策略吗?

4 个解决方案

#1


7  

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

That will certainly work (with some provisos; see below), but I'd use:

这当然会奏效(但有一些附带条件;但我会用:

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

The j length modifier

j长度修改器

Specifies that a following d, i, o, u, x, or X conversion specifier applies to an intmax_t or uintmax_t argument; or that a following n conversion specifier applies to a pointer to an intmax_t argument.

指定以下的d、i、o、u、x或x转换说明符适用于intmax_t或uintmax_t参数;或者下面的n个转换说明符应用于指向intmax_t参数的指针。

There are also z and t modifiers for size_t and ptrdiff_t (and their corresponding signed/unsigned types), respectively.

size_t和ptrdiff_t(以及它们对应的有符号/无符号类型)也分别有z和t修饰符。

And personally, I find the format string macros defined in <inttypes.h> ugly and difficult to remember, which is why I prefer "%ju" or "%jd".

就我个人而言,我发现在 难看难记,所以我更喜欢“%ju”或“%jd”。 中定义的格式字符串宏。h>

As you mentioned, it's helpful to know whether the type (ino_t in this case) is signed or unsigned. If you don't happen to know that, it's possible to figure it out:

正如您所提到的,了解类型(本例中的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');
}

though that may be overkill. If you're sure the type is narrower than 64 bits, you can convert the value to intmax_t, and the value will be preserved. Or you can use uintmax_t and get well-defined results for all values, though printing -1 as 18446744073709551615 (264-1) may be a bit confusing.

尽管这可能有点过头了。如果确定类型小于64位,则可以将值转换为intmax_t,并保留该值。或者,您可以使用uintmax_t,并为所有值获得定义良好的结果,尽管打印-1 as 18446744073709551615(264-1)可能有点令人困惑。

All of this works only if your C implementation supports <stdint.h> and the j length modifier for printf -- i.e., if it supports C99. Not all compilers do so (coughMicrosoftcough). For C90, the widest integer types are long and unsigned long, and you can convert to those and use "%ld" and/or "%lu". You can theoretically test for C99 compliance using the __STDC_VERSION__ predefined macro -- though some pre-C99 compilers might still support types wider than long and unsigned long as an extension.

只有当您的C实现支持 和printf的j长度修饰符——即。,如果它支持C99。不是所有的编译器都这样做(coughMicrosoftcough)。对于C90,最宽的整数类型是长和无符号长,您可以将它们转换为这些类型并使用“%ld”和/或“%lu”。理论上,您可以使用__STDC_VERSION__预定义宏来测试C99遵从性——尽管一些预C99编译器可能仍然支持比long和unsigned更宽的类型作为扩展。 时,所有这些才能工作。h>

#2


5  

The "size" of an integer type is not relevant here, but its range of values.

整数类型的“大小”在这里不相关,而是它的值范围。

As apparently you tried, yet, it is possible to cast to uintmax_t and intmax_t to easily solve any ambiguity in the printf() call.

显然,您已经尝试过了,但是可以对uintmax_t和intmax_t进行强制转换,以便轻松解决printf()调用中的任何歧义。

The issue of the signed or unsigned types can be solved in an easy way:

签名类型或未签名类型的问题可以用一种简单的方式解决:

  • All unsigned integer operations works modulo "N" for some positive value N, depending of the type. It implies that every result involving only unsigned integer types gives a nonnegative value.
  • 所有无符号整型运算对于某些正的值N来说都是模块化的,这取决于类型。它意味着每个只涉及无符号整数类型的结果都给出一个非负值。
  • To detect if a variable x has a signed or an unsigned type, it is enough to verify if x and -x are both nonnegative values.
  • 要检测变量x是否具有带符号类型或无符号类型,只需验证x和-x是否都是非负值即可。

For example:

例如:

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

Now, we can write some macros:

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

(Edited: the name and expression of the macro have changed)

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

 #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: The signed or unsigned character of a variable is not well detected by the macro above when the value is 0. But in this case everything works well, because 0 has the same bit representation in signed or unsigned types.

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

The first macro can be used to detect if the underlying type of an arithmetical object has unsgined type or not.
This result is used in the second macro to choose the way in that the object is printed on screen.

第一个宏可以用来检测算术对象的底层类型是否为unsgated类型。此结果用于第二个宏,以选择对象在屏幕上打印的方式。

1st REEDITION:

REEDITION 1:

  • As Pascal Cuoq pointed out in his comment, the integer promotions has to be taken in accont for unsigned char and short values fiiting in the range of int. This is equivalent to ask if the value is in the rango 0 to INT_MAX.
  • 正如Pascal Cuoq在他的评论中指出的,对于无符号char和short值fiiting在int范围内,必须在accont中进行整数提升。

So I have changed the name of the macro to fits_signed_type.
Also, I have modified the macro to take in account the positive int values.

所以我把宏的名字改成了fits_signed_type。此外,我还修改了宏以考虑正int值。

The macro fits_unsigned_type can tell if an object has unsigned integer type or not in most cases.

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

  • If the value is negative, obviously the type is not unsigned.
  • 如果值为负数,显然该类型不是未签名的。
  • If the value N is positive then
    • if -N is positive, then N has unsigned type,
    • 如果-N是正的,那么N有无符号类型,
    • If -N is negative, but N is in the range 0 to INT_MAX, then the type of N could be signed or unsigned, but it would fit in the range of positive values ofint, which fits in the range of uintmax_t.
    • 如果-N是负的,但N在0到INT_MAX的范围内,那么N的类型可以是有符号的或无符号的,但它将适合于ofint的正值范围,它位于uintmax_t的范围内。
  • 如果N是积极如果N是正的,那么N无符号类型,如果N是负的,但INT_MAX N范围是0,然后N可以签署或无符号的类型,但是它会适合ofint积极的范围值,适合在uintmax_t的范围。

2nd REEDITION:

REEDITION 2:

Ir seems that there are here to approaches to solve the same problem. My approach takes in account the range of values and integer promotion rules to produce the correct printed value with printf(). On the other hand, Grzegorz Szpetkowski's approach determines the signed character of a type in straight form. I like both.

Ir似乎有解决同样问题的方法。我的方法考虑了值的范围和整型提升规则,以使用printf()生成正确的打印值。另一方面,Grzegorz Szpetkowski的方法决定了一种直线型的符号类型。我喜欢这两个。

#3


4  

Since your are already using C99 header, there is a possibility to use exact width format specifier depending on sizeof(T) and signed/unsigned check. This however has to done after preprocessing phase (so sadly ## operator cannot be used here to construct PRI token). Here is an idea:

由于您已经在使用C99头,因此有可能根据sizeof(T)和签名/无签名检查使用精确的宽度格式说明符。然而,这必须在预处理阶段之后完成(因此,遗憾的是##操作符不能在这里用于构造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;
    }

With this method cast is not needed anymore, however format string has to be manually constructed before passing it to printf (i.e. no automatic string concatenation). Here is some working example:

由于这种方法不再需要,所以在将格式字符串传递给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;
}

Note this requires some little trickery of Statement Expr, but there might be some other way (this is the "price" to be generic) as well.

注意,这需要一些语句Expr的小技巧,但是也可能有其他的方法(这是通用的“价格”)。

EDIT:

Here is second version, that doesn't require specific compiler extension (don't worry I can't read it too) using function-like macro:

这是第二个版本,不需要特定的编译器扩展(不用担心我也看不懂),使用函数式宏:

#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;
}

Note that conditional operator has left assiociativity (thus it evalutes from left to right as intended).

注意,条件运算符具有左辅助性(因此它按预期从左到右进行计算)。

#4


3  

Using C11 type generic macros, it is possible to construct the format string at compile time, e.g.:

使用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"));
}

There is a potential problem, though: if there are types distinct from those listed (i.e., other than signed and unsigned versions of char, short, int, long, and long long), this does not work for those types. It is also not possible to add types such as size_t and intmax_t to the type generic macro "just in case", because it will cause an error if they are typedefd from one of the already listed types. I've left the default case unspecified so that the macro generates a compile-time error when no matching type is found.

但是有一个潜在的问题:如果有不同于列出的类型(例如。,除了符号和无符号版本的char、short、int、long和long)之外,这并不适用于这些类型。也不可能将size_t和intmax_t等类型添加到泛型宏类型“just in case”中,因为如果它们是来自已列出的类型之一的typedefd,则会导致错误。我保留了未指定的默认情况,以便在没有找到匹配类型时宏生成编译时错误。

However, as seen in the example program, size_t and intmax_t work just fine on platforms where they are the same as one of the listed types (e.g., same as long). Similarly there is no issue if, e.g., long and long long, or long and int, are the same type. But a safer version might be to just cast to intmax_t or uintmax_t according to signedness (as seen in other answers), and make a type generic macro with only those options…

但是,正如在示例程序中看到的,size_t和intmax_t在与列出的类型之一(例如,与long相同)相同的平台上运行良好。同样,如果long and long long long, long and int都是同一类型,也没有问题。但是更安全的版本可能是根据签字率(如其他答案所示)将其强制转换为intmax_t或uintmax_t,然后创建一个只有这些选项的泛型宏……

A cosmetic problem is that the type generic macro expands with parentheses around the string literal, preventing concatenation with adjacent string literals in the usual way. This prevents things like:

一个美观的问题是,类型泛型宏在字符串字面上以圆括号展开,以通常的方式防止与相邻字符串的连接。这可以防止诸如:

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

Hence the PRIFMT macro with prefix and suffix included for the common case of printing a single variable:

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

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

(Note that it would be simple to expand this macro with the non-integer types supported by printf, e.g., double, char *…)

(注意,用printf支持的非整数类型扩展这个宏很简单,例如,double, char *…)

#1


7  

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

That will certainly work (with some provisos; see below), but I'd use:

这当然会奏效(但有一些附带条件;但我会用:

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

The j length modifier

j长度修改器

Specifies that a following d, i, o, u, x, or X conversion specifier applies to an intmax_t or uintmax_t argument; or that a following n conversion specifier applies to a pointer to an intmax_t argument.

指定以下的d、i、o、u、x或x转换说明符适用于intmax_t或uintmax_t参数;或者下面的n个转换说明符应用于指向intmax_t参数的指针。

There are also z and t modifiers for size_t and ptrdiff_t (and their corresponding signed/unsigned types), respectively.

size_t和ptrdiff_t(以及它们对应的有符号/无符号类型)也分别有z和t修饰符。

And personally, I find the format string macros defined in <inttypes.h> ugly and difficult to remember, which is why I prefer "%ju" or "%jd".

就我个人而言,我发现在 难看难记,所以我更喜欢“%ju”或“%jd”。 中定义的格式字符串宏。h>

As you mentioned, it's helpful to know whether the type (ino_t in this case) is signed or unsigned. If you don't happen to know that, it's possible to figure it out:

正如您所提到的,了解类型(本例中的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');
}

though that may be overkill. If you're sure the type is narrower than 64 bits, you can convert the value to intmax_t, and the value will be preserved. Or you can use uintmax_t and get well-defined results for all values, though printing -1 as 18446744073709551615 (264-1) may be a bit confusing.

尽管这可能有点过头了。如果确定类型小于64位,则可以将值转换为intmax_t,并保留该值。或者,您可以使用uintmax_t,并为所有值获得定义良好的结果,尽管打印-1 as 18446744073709551615(264-1)可能有点令人困惑。

All of this works only if your C implementation supports <stdint.h> and the j length modifier for printf -- i.e., if it supports C99. Not all compilers do so (coughMicrosoftcough). For C90, the widest integer types are long and unsigned long, and you can convert to those and use "%ld" and/or "%lu". You can theoretically test for C99 compliance using the __STDC_VERSION__ predefined macro -- though some pre-C99 compilers might still support types wider than long and unsigned long as an extension.

只有当您的C实现支持 和printf的j长度修饰符——即。,如果它支持C99。不是所有的编译器都这样做(coughMicrosoftcough)。对于C90,最宽的整数类型是长和无符号长,您可以将它们转换为这些类型并使用“%ld”和/或“%lu”。理论上,您可以使用__STDC_VERSION__预定义宏来测试C99遵从性——尽管一些预C99编译器可能仍然支持比long和unsigned更宽的类型作为扩展。 时,所有这些才能工作。h>

#2


5  

The "size" of an integer type is not relevant here, but its range of values.

整数类型的“大小”在这里不相关,而是它的值范围。

As apparently you tried, yet, it is possible to cast to uintmax_t and intmax_t to easily solve any ambiguity in the printf() call.

显然,您已经尝试过了,但是可以对uintmax_t和intmax_t进行强制转换,以便轻松解决printf()调用中的任何歧义。

The issue of the signed or unsigned types can be solved in an easy way:

签名类型或未签名类型的问题可以用一种简单的方式解决:

  • All unsigned integer operations works modulo "N" for some positive value N, depending of the type. It implies that every result involving only unsigned integer types gives a nonnegative value.
  • 所有无符号整型运算对于某些正的值N来说都是模块化的,这取决于类型。它意味着每个只涉及无符号整数类型的结果都给出一个非负值。
  • To detect if a variable x has a signed or an unsigned type, it is enough to verify if x and -x are both nonnegative values.
  • 要检测变量x是否具有带符号类型或无符号类型,只需验证x和-x是否都是非负值即可。

For example:

例如:

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

Now, we can write some macros:

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

(Edited: the name and expression of the macro have changed)

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

 #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: The signed or unsigned character of a variable is not well detected by the macro above when the value is 0. But in this case everything works well, because 0 has the same bit representation in signed or unsigned types.

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

The first macro can be used to detect if the underlying type of an arithmetical object has unsgined type or not.
This result is used in the second macro to choose the way in that the object is printed on screen.

第一个宏可以用来检测算术对象的底层类型是否为unsgated类型。此结果用于第二个宏,以选择对象在屏幕上打印的方式。

1st REEDITION:

REEDITION 1:

  • As Pascal Cuoq pointed out in his comment, the integer promotions has to be taken in accont for unsigned char and short values fiiting in the range of int. This is equivalent to ask if the value is in the rango 0 to INT_MAX.
  • 正如Pascal Cuoq在他的评论中指出的,对于无符号char和short值fiiting在int范围内,必须在accont中进行整数提升。

So I have changed the name of the macro to fits_signed_type.
Also, I have modified the macro to take in account the positive int values.

所以我把宏的名字改成了fits_signed_type。此外,我还修改了宏以考虑正int值。

The macro fits_unsigned_type can tell if an object has unsigned integer type or not in most cases.

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

  • If the value is negative, obviously the type is not unsigned.
  • 如果值为负数,显然该类型不是未签名的。
  • If the value N is positive then
    • if -N is positive, then N has unsigned type,
    • 如果-N是正的,那么N有无符号类型,
    • If -N is negative, but N is in the range 0 to INT_MAX, then the type of N could be signed or unsigned, but it would fit in the range of positive values ofint, which fits in the range of uintmax_t.
    • 如果-N是负的,但N在0到INT_MAX的范围内,那么N的类型可以是有符号的或无符号的,但它将适合于ofint的正值范围,它位于uintmax_t的范围内。
  • 如果N是积极如果N是正的,那么N无符号类型,如果N是负的,但INT_MAX N范围是0,然后N可以签署或无符号的类型,但是它会适合ofint积极的范围值,适合在uintmax_t的范围。

2nd REEDITION:

REEDITION 2:

Ir seems that there are here to approaches to solve the same problem. My approach takes in account the range of values and integer promotion rules to produce the correct printed value with printf(). On the other hand, Grzegorz Szpetkowski's approach determines the signed character of a type in straight form. I like both.

Ir似乎有解决同样问题的方法。我的方法考虑了值的范围和整型提升规则,以使用printf()生成正确的打印值。另一方面,Grzegorz Szpetkowski的方法决定了一种直线型的符号类型。我喜欢这两个。

#3


4  

Since your are already using C99 header, there is a possibility to use exact width format specifier depending on sizeof(T) and signed/unsigned check. This however has to done after preprocessing phase (so sadly ## operator cannot be used here to construct PRI token). Here is an idea:

由于您已经在使用C99头,因此有可能根据sizeof(T)和签名/无签名检查使用精确的宽度格式说明符。然而,这必须在预处理阶段之后完成(因此,遗憾的是##操作符不能在这里用于构造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;
    }

With this method cast is not needed anymore, however format string has to be manually constructed before passing it to printf (i.e. no automatic string concatenation). Here is some working example:

由于这种方法不再需要,所以在将格式字符串传递给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;
}

Note this requires some little trickery of Statement Expr, but there might be some other way (this is the "price" to be generic) as well.

注意,这需要一些语句Expr的小技巧,但是也可能有其他的方法(这是通用的“价格”)。

EDIT:

Here is second version, that doesn't require specific compiler extension (don't worry I can't read it too) using function-like macro:

这是第二个版本,不需要特定的编译器扩展(不用担心我也看不懂),使用函数式宏:

#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;
}

Note that conditional operator has left assiociativity (thus it evalutes from left to right as intended).

注意,条件运算符具有左辅助性(因此它按预期从左到右进行计算)。

#4


3  

Using C11 type generic macros, it is possible to construct the format string at compile time, e.g.:

使用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"));
}

There is a potential problem, though: if there are types distinct from those listed (i.e., other than signed and unsigned versions of char, short, int, long, and long long), this does not work for those types. It is also not possible to add types such as size_t and intmax_t to the type generic macro "just in case", because it will cause an error if they are typedefd from one of the already listed types. I've left the default case unspecified so that the macro generates a compile-time error when no matching type is found.

但是有一个潜在的问题:如果有不同于列出的类型(例如。,除了符号和无符号版本的char、short、int、long和long)之外,这并不适用于这些类型。也不可能将size_t和intmax_t等类型添加到泛型宏类型“just in case”中,因为如果它们是来自已列出的类型之一的typedefd,则会导致错误。我保留了未指定的默认情况,以便在没有找到匹配类型时宏生成编译时错误。

However, as seen in the example program, size_t and intmax_t work just fine on platforms where they are the same as one of the listed types (e.g., same as long). Similarly there is no issue if, e.g., long and long long, or long and int, are the same type. But a safer version might be to just cast to intmax_t or uintmax_t according to signedness (as seen in other answers), and make a type generic macro with only those options…

但是,正如在示例程序中看到的,size_t和intmax_t在与列出的类型之一(例如,与long相同)相同的平台上运行良好。同样,如果long and long long long, long and int都是同一类型,也没有问题。但是更安全的版本可能是根据签字率(如其他答案所示)将其强制转换为intmax_t或uintmax_t,然后创建一个只有这些选项的泛型宏……

A cosmetic problem is that the type generic macro expands with parentheses around the string literal, preventing concatenation with adjacent string literals in the usual way. This prevents things like:

一个美观的问题是,类型泛型宏在字符串字面上以圆括号展开,以通常的方式防止与相邻字符串的连接。这可以防止诸如:

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

Hence the PRIFMT macro with prefix and suffix included for the common case of printing a single variable:

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

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

(Note that it would be simple to expand this macro with the non-integer types supported by printf, e.g., double, char *…)

(注意,用printf支持的非整数类型扩展这个宏很简单,例如,double, char *…)