将size_t转换为unsigned long int是否安全?

Aug*_*rom 12 c c89

我需要一种可移植的方式来打印类型变量n的值size_t.由于我使用ANSI C89,我不能使用z长度修饰符.我目前的做法是将价值投射到long unsigned int:

printf("%lu\n", (long unsigned int) n);
Run Code Online (Sandbox Code Playgroud)

只要size_t被定义为unsigned int或者long unsigned int我看不出它会如何失败.演员安全吗?

Bil*_*uya 16

C89中,size_t被定义为无符号整数类型.与未来的标准不同,C89定义了无符号整数类型列表如下:

  • 无符号的字符
  • 未签约的短片
  • unsigned int
  • 无符号长

因此,size_t在C89中永远不会大于unsigned long,因此强制转换总是安全的 - 因为它不会导致任何未定义的行为,并且它总是足够大以保持整个值.

值得注意; C89标准规定: "符合标准的实现可能具有扩展(包括附加库函数),前提是它们不会改变任何严格符合程序的行为" 意味着没有扩展可以改变这种行为 - 同时仍然符合C89标准,如已经专门列出了无符号整数类型,因此无法更改.

在未来的标准中,这不是保证,虽然您不会得到未定义的行为 - 您可能会丢失unsigned long小于的数据size_t,这意味着您将向用户显示不正确的数据.在这种情况下,我会犹豫是否将其标记为"安全".


作为重要的补充说明; 这个答案指的是符合C89标准的编译器.您的C89编译器可能在上述方面"低于兼容",在这种情况下 - 将行为视为类似于C99或更新的行为,您将看不到未定义的行为,但如果size_t是,则可能会遭受数据丢失大于unsigned long.但要明确,这不符合C89标准.

除此之外,虽然我对标准(1.7合规性)的解释是,虽然它规定扩展不得改变"严格符合程序"的行为,因此不能改变size_t必须unsigned long在没有遵守的情况下最大的事实; 它并没有改变这种扩展确实存在的事实.例如,GNU GCC确实提供了添加的扩展unsigned long long.在我看来,这是不合规的,但现实是你必须准备好处理这样的事情 - 同时标准说你正在做的事情是完全安全的,你必须准备好在不合规的情况下潜在的数据丢失使用编译器或扩展.


有关此主题的讨论,请参阅此处:https: //stackoverflow.com/a/39441237/955340

  • “因此,C89中的size_t永远不会大于无符号长,因此强制转换始终是安全的”不是事实。标准中没有这样的保证。在C89中,将size_t作为64位类型,将unsigned long作为32位类型是有效的。话虽这么说,但是如果坚持使用C89,没有比这更好的选择了。 (2认同)
  • 这是基于这样的假设,即:在我不确定的是C89。也许MSVC在这方面不是有效的C89实现;-) (2认同)
  • “ size_t”整数类型和整数类型定义为“类型char,有符号和无符号整数类型以及枚举类型统称为整数类型”。因此,如果将“ size_t”定义为“ unsigned long long”(例如C89中的实现提供的扩展名),则它是“ unsigned integer type”,可以视为整数类型。因此,尚不清楚size_t是否必须为*现有*整数类型。 (2认同)
  • 这个答案的理由是不正确的。size_t可能是扩展的整数类型(在这种情况下,C89中可能无法使用printf进行打印)。也可能只是为了跨平台兼容性而编写C89代码,而是将其与可能有较大标准类型的C99或C11编译器一起使用。在C99或更高版本中,您可以安全地对`(uintmax_t)`和`%ju`使用强制类型转换,但是C89没有。 (2认同)
  • @Bilkokuya:据我所知,C89允许`size_t`是扩展的整数类型的实现。接受“ long long”作为扩展名并使用“ unsigned long long”定义“ size_t”的C89应该是这样的一个例子。严格来说,C99实现确实不是符合要求的C89实现(由于命名空间方面的考虑和行为上的细微差异),但从实际角度出发,编写既是有效的C99代码又可以在C99实现上编译的C89代码是合理的。 (2认同)