26

I am using the mingw-w64 (x64) fork of minGW as prepared on nuwen.net. This is from the 7.1 version of gcc :

gcc --version
gcc (GCC) 7.1.0

I am compiling this program:

#include <stdio.h>

int main(void)
{
    size_t a = 100;
    printf("a=%lu\n",a);
    printf("a=%llu\n",a);
    printf("a=%zu\n",a);
    printf("a=%I64u\n",a);
}

with warnings and c11 standard:

gcc -Wall -Wextra -Wpedantic -std=c11 test_size_t.c

and I get these warnings:

   test_size_t.c: In function 'main':
    test_size_t.c:6:14: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t {aka long long unsigned int}' [-Wformat=]
      printf("a=%lu\n",a);
                ~~^
                %I64u
    test_size_t.c:6:14: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t {aka long long unsigned int}' [-Wformat=]
      printf("a=%lu\n",a);
                ~~^
                %I64u
    test_size_t.c:7:14: warning: unknown conversion type character 'l' in format [-Wformat=]
      printf("a=%llu\n",a);
                  ^
    test_size_t.c:7:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%llu\n",a);
             ^~~~~~~~~~
    test_size_t.c:7:14: warning: unknown conversion type character 'l' in format [-Wformat=]
      printf("a=%llu\n",a);
                  ^
    test_size_t.c:7:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%llu\n",a);
             ^~~~~~~~~~
    test_size_t.c:8:13: warning: unknown conversion type character 'z' in format [-Wformat=]
      printf("a=%zu\n",a);
                 ^
    test_size_t.c:8:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%zu\n",a);
             ^~~~~~~~~
    test_size_t.c:8:13: warning: unknown conversion type character 'z' in format [-Wformat=]
      printf("a=%zu\n",a);
                 ^
    test_size_t.c:8:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%zu\n",a);
             ^~~~~~~~~
    test_size_t.c:9:9: warning: ISO C does not support the 'I64' ms_printf length modifier [-Wformat=]
      printf("a=%I64u\n",a);
         ^~~~~~~~~~~
    test_size_t.c:9:9: warning: ISO C does not support the 'I64' ms_printf length modifier [-Wformat=]

I would like to printf a size_t without warning but don't know the correct format specifier in this situation.

Scooter
  • 6,802
  • 8
  • 41
  • 64

3 Answers3

28

The problem is not the compiler but the C library. MinGW uses Microsoft's "Visual C Runtime" (msvcrt) which only conforms to and it doesn't support the z format specifier.

Here's what you can do to safely print a size_t when using MinGW:

#include <inttypes.h>
#include <stdio.h>

#ifdef _WIN32
#  ifdef _WIN64
#    define PRI_SIZET PRIu64
#  else
#    define PRI_SIZET PRIu32
#  endif
#else
#  define PRI_SIZET "zu"
#endif

int main(void)
{
    size_t mySize = 24;

    printf("%" PRI_SIZET "\n", mySize);
}

On win64, you would get a warning with this code, because PRIu64 expands to the msvcrt-specific I64u format specifier. But you can silence this warning with the GCC flag -Wno-pedantic-ms-format.


Note that you need a similar trick for long long (here using PRIu64 on both 32bit and 64bit windows) because msvcrt doesn't know ll either.


edit: as pointed out by @M.M in a comment, you can instead link MinGW-provided alternative stdio functions that support C11 with #define __USE_MINGW_ANSI_STDIO 1. I prefer not to link extra code if I can get around the peculiarities of msvcrt, but that's of course a matter of taste.

  • 3
    `%zu` is supported on Visual Studio 2013 and later. Check for it with `_MSC_VER >= 1800`. And your claim that MSVC doesn't support `ll` is *completely* false; that support has been there for *many* versions, so many I can't remember when it was not supported. – Cody Gray - on strike Jun 06 '17 at 10:07
  • 3
    @CodyGray I just checked: `%zu` is NOT supported by the `msvcrt.dll` version 7.0.10586.0 found on my windows 10 system. So I guess MSVC does something similar like MinGW with `__USE_MINGW_ANSI_STDIO` and links some extra code to support this? –  Jun 06 '17 at 11:10
  • 2
    Well, no, the difference is that MSVC hasn't linked to `msvcrt.dll` for like 20 years. The last version to use that was VC++ 6. I do remember knowing this, but I'd somehow forgotten that MinGW was linking to Microsoft's private runtime libraries. I guess it's linking using the DDK. Updates to `msvcrt.dll` are not documented, because the DLL is not intended for public use. I guess its `printf` support hasn't been expanded because Microsoft doesn't use it. Well, it's not as if anyone needed *more* reasons not to use MinGW! – Cody Gray - on strike Jun 06 '17 at 12:21
  • 1
    This of course is very opinionated, and even if Microsoft doesn't *want* you to use this "original" `msvcrt`, it *is* used by applications that come with windows and of course it will stay compatible to the old "vc6" version, because it can't break old applications. So if you stick to this functionality, there's not much that could go wrong and you don't need to "bloat" your product with a full runtime. The many runtime versions in existence aren't a great solution to me, but of course, this is also just an opinion. –  Jun 06 '17 at 12:35
  • You can disable the warning with `#pragma`s, too. – S.S. Anne Aug 31 '19 at 21:12
11

The alternative solution as mentioned in comments is to toss in the __USE_MINGW_ANSI_STDIO compiler switch:

#define __USE_MINGW_ANSI_STDIO 1

#include <stdio.h>

int main(void)
{
    size_t a = 100;
    printf("a=%lu\n",a);
    printf("a=%llu\n",a);
    printf("a=%zu\n",a);
    printf("a=%I64u\n",a);
}

This makes the code compile as expected and gcc now gives the appropriate warnings:

warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t' [-Wformat=]  
warning: ISO C does not support the 'I' printf flag [-Wformat=]  
warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'size_t' [-Wformat=]  

Alternatively you can define the macro on command line with -D__USE_MINGW_ANSI_STDIO=1

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    Important to note - do the #include before you actualy include stdio.h. I like to first do my includes, and then my macros, and this baffeled me for a bit because defining the macro in the command line worked, but in code not... Until I noticed the obvious. – kowalski5233 Sep 21 '21 at 12:19
3

PRIuPTR trick works (Cross platform format string for variables of type size_t?):

#include <stdio.h>
#include <inttypes.h>
int main()
{
    size_t a = (size_t)(1024ULL * 1024 * 1024 * 1024 + 13);
    printf("a                 = %" PRIuPTR "\n", a);
    printf("sizeof(size_t)    = %" PRIuPTR "\n", sizeof(size_t));
    printf("sizeof(uintptr_t) = %" PRIuPTR "\n", sizeof(uintptr_t));
    return 0;
}

Output x86:

a                 = 13
sizeof(size_t)    = 4
sizeof(uintptr_t) = 4

Output x64:

a                 = 1099511627789
sizeof(size_t)    = 8
sizeof(uintptr_t) = 8
infval
  • 318
  • 3
  • 4