【问题标题】:Can I pass a char where an integer is expected in printf?我可以在 printf 中期望整数的地方传递一个字符吗?
【发布时间】:2012-07-11 11:51:23
【问题描述】:

下面的代码正确吗?

char mychar = 200;
printf("%x", mychar);

根据http://www.cplusplus.com/reference/clibrary/cstdio/printf/ %x 需要一个整数(我的编译器为 4 个字节),而我在这里只传递 1 个字节。由于printf 使用可变参数,我担心这仅适用于堆栈上的字节对齐(即,一个字符在压入堆栈时总是使用 4 个字节)。

我觉得这样写比较好:

char mychar = 200;
printf("%x", static_cast<int>(mychar));

你认为第一个代码是安全的吗?如果不是,你认为如果我切换到 bigendian 架构,我会得到不同的输出吗?

【问题讨论】:

  • 就我个人而言,我会谨慎行事并写(int)mychar;在最坏的情况下我浪费了五个字符,在最好的情况下我避免了 UB。此外,与语言的尘土飞扬的角落一样,我更喜欢更加清晰/安全,以避免让我的代码的未来读者感到困惑。
  • 有趣的问题,printf 是否隐式转换?使用 static_cast 你总是安全的
  • @Paranaix:这不是 printf 本身,而是提升某些类型的可变参数(例如,float 总是提升为 double)。
  • @Matteo Italia 好吧,我宁愿考虑函数本身的转换(因此取决于实现)而不是调用本身的转换

标签: c++ c variadic-functions


【解决方案1】:

在您的示例中,参数实际上是int 类型。由于默认参数提升mychar 被提升为 int

(C99, 6.5.2.2p6) “如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并且浮点类型的参数提升为双精度。 这些被称为默认参数提升。"

和(强调我的):

(C99, 6.5.2.2p7) "如果表示被调用函数的表达式的类型确实包含原型,参数会隐式转换为类型,就像通过赋值一样对应参数的类型,将每个参数的类型作为其声明类型的非限定版本。函数原型声明器中的省略号表示会导致参数类型转换在最后一个声明的参数之后停止。默认参数 提升是在尾随参数上执行的。”

请注意,从技术上讲,x 转换说明符需要 unsigned int 参数,但 intunsigned int 类型保证具有相同的表示形式。

【讨论】:

  • @Naveen:是的,C++11 5.2.2/7 或多或少说了同样的话。
  • @Naveen:是的,差不多。 C++ 有时会有不同的类型(例如bool),它们有自己的提升规则,但除此之外都是一样的。
  • 我明白了。但是我们在这里使用 C++。你能确认 C++ 标准也这么说吗?
  • @happy_emi 正如 Mike 所说,C++ 在 C++11、5.2.2p7 中具有基本相同的规则,并且也将它们称为默认参数提升。
  • 如果我错了,请纠正我,但是当 sizeof(int)==1char 默认为 unsigned 时,char 不会提升为 int 而是 unsigned int。跨度>
【解决方案2】:

这仅适用于您,因为您的平台能够将 int(您的 char 升级为 unsigned)解释为 unsigned,这是 %x 所期望的。为了确保这始终有效,您应该使用适当的格式说明符,即%d

由于您似乎想使用char 作为数字量,因此最好使用signed charunsigned char 这两种替代类型来明确您的意图。 char 可以是您平台功能的有符号或无符号类型。 unsigned char 的正确说明符将是 %hhx

【讨论】:

  • 不使用%d 仅在char 已签名的假设下可靠工作?
  • C 表示 intunsigned int 在 C99 6.2.5p6 中具有相同的大小和相同的对齐要求。
  • @larsmans,它只能在char 未签名并且unsigned int 具有相同宽度的假设架构上失败。在所有其他架构上,char 提升为 int 而不是 unsigned int
  • @ouah,是的,但是unsigned int 可能有填充位和陷阱表示。虽然这不太可能,而且在从char 推广时更不可能遇到这样的陷阱表示:)
  • @JensGustedt:啊,我明白了。谢谢!
【解决方案3】:

只需使用printf("%hhx", mychar)。请不要使用 cplusplus.com 作为参考。这个问题只是证明了它包含许多错误和遗漏大量信息的声誉。

【讨论】:

    【解决方案4】:

    如果您使用的是 C++,那么您可能希望使用流。

    您需要将字符转换为整数,否则它们将被打印为字形。另外,有符号的字符会在转换过程中进行符号扩展,所以最好使用无符号字符。例如,持有 0xFF 的字符将打印为 0xFFFFFFFF,除非先转换为无符号字符。

    #include <iostream>
    #include <iomanip>
    
    int main(void){
    
        char c = 0xFF;
        std::cout << std::hex << std::setw(2) << std::setfill('0');
        std::cout << static_cast<int>(static_cast<unsigned char>(c)) << std::endl;
    
        return 0;
    }
    

    【讨论】:

    • 离题 - 有充分的理由继续在 C++ 中使用 stdio。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 2012-05-12
    • 2012-08-20
    • 1970-01-01
    相关资源
    最近更新 更多