0

My test code:

#include <cstdint>
#include <cstdio>

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

Here is how I compiled with GCC 8.2:

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

And here is GCC's output:

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);
         ^~~~

Tho, if I try to print an uint32_t, it has no error/warning.

I wonder why GCC expects uint8_t to be signed int.

Thanks.

HCSF
  • 1,634
  • 9
  • 23
  • this duplicates multiple questions: [Why is the format specifier for uint8_t and uint16_t the same (%u)?](https://stackoverflow.com/q/26362386/995714) – phuclv Apr 30 '19 at 03:58

3 Answers3

1

Default argument promotions are applied to operands of a variadic function. Under these, an expression of type unsigned char is promoted to int.

Igor Tandetnik
  • 45,980
  • 4
  • 51
  • 75
  • Thanks. I guess no way to ask GCC to verify `printf()` completely (including sign here) by ignoring the change of sign due to integral promotion? I have my own custom `printf()` but it doesn't use variadic function. However, I would like to leverage GCC's checks. – HCSF Apr 30 '19 at 03:50
  • I'm not sure I quite understand your question. In what way do you believe the verification is incomplete? The program shown exhibits undefined behavior, by way of passing an `int` where `unsigned int` is expected. The fact that GCC catches this is a good thing. – Igor Tandetnik Apr 30 '19 at 03:53
1

In C and C++ types narrower than int are always promoted to int. See Why must a short be converted to an int before arithmetic operations in C and C++?

And inside variadic functions default promotion also applies, which means you can't pass types narrower than int to vararg functions. So uint8_t must be printed with %d, not %u. But anyway you're printing it the wrong way. The correct way is to use PRIu8

printf("%" PRIu8 "\n", x);
phuclv
  • 27,258
  • 11
  • 104
  • 360
0

To print a uint8_t variable with printf(), you should do something like the following:

#include <cinttypes>
#include <cstdio>

int print_u8(std::uint8_t x) {
  return std::printf("%" PRIu8 "\n", x);
}

The <cinttypes> header includes printf and scanf format specifiers for all the <cstdint> types (and explicitly includes that header) that should be used for maximum portability.

Shawn
  • 28,389
  • 3
  • 10
  • 37
  • This is quite interesting. I just tried `printf(PRIu8)` and it printed `u`. So the specifier is still `%u`; however, GCC would happily check it even with the integral promotion. – HCSF Apr 30 '19 at 04:07
  • I wish we could just use `%u` as at the end `"%" PRIu8` gives `%u` anyway. – HCSF Apr 30 '19 at 04:09
  • sorry for accepted and then unaccepted because I just tried, same warning. – HCSF Apr 30 '19 at 04:13
  • https://sourceware.org/bugzilla/show_bug.cgi?id=5747 – HCSF Apr 30 '19 at 04:17