【问题标题】:C: figuring out % format specifiers for typedefs without gcc warnings? [duplicate]C:在没有 gcc 警告的情况下找出 typedef 的 % 格式说明符? [复制]
【发布时间】:2021-10-06 05:39:15
【问题描述】:

尝试在 C 中使用 typedef'd 变量时遇到的一个问题是弄清楚实际的原始类型是 typedef'd 时,我可以使用适当的 % 说明符尝试使用printf 打印其值。大多数时候我不得不参考gcc 产生的警告。例如,当使用stat 系统调用时,返回的结构有很多具有不同typedef'd 类型的成员,如dev_tino_tmode_t 等,我通常不得不猜测是什么格式说明符可能是然后编译器警告我并纠正它:

stat_demo.c: In function ‘main’:
stat_demo.c:46:45: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘__blksize_t’ {aka ‘long int’} [-Wformat=]
   46 |     printf("block size for file system IO: %d\n", s->st_blksize);
      |                                            ~^     ~~~~~~~~~~~~~
      |                                             |      |
      |                                             int    __blksize_t {aka long int}
      |                                            %ld

这是大多数人在打印 typedef 时使用的方式吗?由于您需要知道typedef'd 的实际原语是什么,这难道不是破坏重点吗?有没有更好的方法来完全抽象实际类型?

【问题讨论】:

  • 您可以为自己的域类型定义完全自定义的printf() 样式函数。然后,您可以选择具有任意语义上有意义的说明符。当然,不会神奇地适用于 other 的域类型,例如 dev_t 等等。 :)
  • 你没有显示类型。当您使用size_t 时,编译器可能会将其称为long int,但格式说明符应为%zu。如果有疑问,请转换为可用的最大整数类型并使用其说明符。
  • 现在你明白了为什么在没有需要的情况下定义类型是不好的。
  • 这就是所谓的“泄漏抽象”,因为类型的细节必须“泄漏”出来才能正确使用它。我发现处理它的最好方法是提供一个单独的 API,将该类型的值格式化为字符串,然后使用 %s - printf( "%s\n", fmt__blksize( s->st_blksize, buffer, sizeof buffer ) );将该 API 调用的结果传递给 printf >

标签: c typedef


【解决方案1】:

这不是说错了吗?

是的,你是对的,确实如此。

有没有更好的方法来完全抽象实际类型?

不,不是。

根本的问题是,如果你想要一种适当的、抽象的、类型安全的打印任意东西的方法,C 的printf 不是。一种方法是使用 C++ ostream——因为任何类型都可以重载 operator<<——尽管 C++ ostream 也不一定是完美的。

由于这里没有完美的答案,它最终成为一个非常主观的问题。这就是我所做的,因为它的价值:

  1. 首先,我不使用那么多 typedef,当然也不用于数字类型。
  2. 如果我有size_t,我会用%zu 打印它。
  3. 对于大多数其他类型定义——尤其是像off_t 这样的类型——我转换为unsigned long,并使用%lu 打印。

第 3 号显然是不完美的,理论上会使某人尝试实现更大的 off_t 类型的尝试无效,可能相当于 long long 的类型。一种更安全的方法——我应该养成这样做的习惯——是转换为 intmax_tuintmax_t,然后使用 %jd%ju 打印。

打印固定宽度类型的“官方”方法(如果您知道这是您所拥有的)是使用来自<inttypes.h> 的宏之一,如下所示:

uint32_t n;
printf("%" PRIX32 "\n", n);

我认为有 PRIxxx 宏用于文件系统相关的类型定义,例如 off_t,但可能没有。

另见How should I print types like off_t and size_t?

【讨论】:

  • 这不是理论上的问题:off_t 在 32 位 Linux 上是 long long,而 _FILE_OFFSET_BITS#defined 为 64(建议用于所有新程序)跨度>
  • @o11c 对。所以我只会在我知道文件不会很大时才使用它。 (著名的遗言,我知道!:-))
  • @o11c _FILE_OFFSET_BITS 是一个可怕的、可怕的组合,当然。我知道它为什么会存在,但这是不从历史中学习的典型例子。如果每个人都记得从seek()lseek() 的痛苦......
【解决方案2】:

这些类型没有独立于系统的定义。 POSIX 只说它们是“适当长度的算术类型”。因此,即使您从编译器警告中推断出您的系统上的 dev_tint,当您移动到其他系统时,它可能是 unsigned shortlong int,甚至理论上是 @987654326,您也会遇到问题@。

通常您通过强制转换来处理这个问题,因此传递给printf 的内容至少是已知类型。 uintmax_t 在这方面很方便,因为它当然应该足够大,并且它有一个格式说明符 %ju。所以你可以写:

printf("block size for file system IO: %ju\n", (uintmax_t)(s->st_blksize));

【讨论】:

  • 可以使用_Generic获取基类型。
猜你喜欢
  • 1970-01-01
  • 2020-07-21
  • 2018-10-30
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 2018-09-16
  • 2011-05-23
  • 1970-01-01
相关资源
最近更新 更多