【问题标题】:GCC interprets uint8_t and uint16_t as signed? [duplicate]GCC 将 uint8_t 和 uint16_t 解释为已签名? [复制]
【发布时间】:2019-09-18 16:26:02
【问题描述】:

我的测试代码:

#include <cstdint>
#include <cstdio>

int main() {
    const constexpr uint8_t x = 64;
    printf("%u", x);
}

这是我使用 GCC 8.2 编译的方式:

g++ -Wall test_format.cpp -o test_format -O3 -std=c++17 -Wformat-signedness

这是 GCC 的输出:

test_format.cpp: In function ‘int main()’:
test_format.cpp:6:9: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int’ [-Wformat=]
  printf("%u", x);
         ^~~~

但是,如果我尝试打印 uint32_t,它没有错误/警告。

我想知道为什么 GCC 期望 uint8_t 被签名为 int。

谢谢。

【问题讨论】:

标签: linux c++11 gcc


【解决方案1】:

Default argument promotions 应用于可变参数函数的操作数。在这些情况下,unsigned char 类型的表达式被提升为 int

【讨论】:

  • 谢谢。我想没有办法让GCC完全验证printf()(包括这里的签名)而忽略因积分提升引起的符号变化?我有自己的自定义printf(),但它不使用可变参数函数。但是,我想利用 GCC 的检查。
  • 我不确定我是否完全理解您的问题。您认为验证不完整的方式有哪些?显示的程序表现出未定义的行为,通过传递int,其中预期unsigned int。 GCC 捕捉到这一点是一件好事。
【解决方案2】:

在 C 和 C++ 中,比 int 窄的类型总是被提升为 int。见Why must a short be converted to an int before arithmetic operations in C and C++?

在可变参数函数内部,默认提升也适用,这意味着您不能将比 int 更窄的类型传递给可变参数函数。所以uint8_t必须用%d打印,而不是%u。但无论如何,你以错误的方式打印它。正确的方法是使用PRIu8

printf("%" PRIu8 "\n", x);

【讨论】:

    【解决方案3】:

    要使用printf() 打印uint8_t 变量,您应该执行以下操作:

    #include <cinttypes>
    #include <cstdio>
    
    int print_u8(std::uint8_t x) {
      return std::printf("%" PRIu8 "\n", x);
    }
    

    &lt;cinttypes&gt; 标头包含所有 &lt;cstdint&gt; 类型(并明确包括该标头)的 printf 和 scanf 格式说明符,应该用于最大程度的可移植性。

    【讨论】:

    • 这很有趣。我刚试过printf(PRIu8),它打印了u。所以说明符还是%u;然而,GCC 会很乐意检查它,即使是积分提升。
    • 我希望我们可以在最后使用%u,因为"%" PRIu8 无论如何都会给%u
    • 很抱歉接受了然后不接受,因为我刚刚尝试过,同样的警告。
    猜你喜欢
    • 2020-01-11
    • 2014-07-23
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-27
    相关资源
    最近更新 更多