C Program Shows %Zu After Conversion to Windows

C program shows %zu after conversion to Windows

As suggested by the bug report discussion linked in the comments, Microsoft's printf functions do not support C99. The mingw-w64 project provides alternative functions that may be used as if they were the normal C99 functions if the macro __USE_MINGW_ANSI_STDIO is set to 1 either before including any headers or on the command line. They support the standard %zu, %jd, etc. format specifiers that even the newest MSVCRT versions do not. You may invoke the function directly using mingw_printf, but it is usually easier to just define the aforementioned macro to 1 and call printf, etc.

It is worth noting that if you use Microsoft's snprintf, it will return -1 to indicate truncation if the buffer is not large enough, unless the buffer and buffer size parameters are NULL and 0 respectively, in which case the number of bytes that would be output is returned. C99 behavior is to always return the number of bytes that would be output if the buffer was sufficiently large, or a negative value if an encoding error occurs, and the mingw-w64 implementation seems to behave correctly according to C99.

And all you need to do to get all of this standard behavior is either #define __USE_MINGW_ANSI_STDIO 1 before any includes if you use any of the printf functions or simply add -D__USE_MINGW_ANSI_STDIO=1 to your compiler invocation.

If you are worried about the macro interfering with other platforms, no other implementation except the original (legacy?) MinGW[32] project that provided similar functionality should actually make use of this preprocessor macro, so it is safe to define it unconditionally.

How to get MinGW GCC to recognize the %zu format specifier for size_t?

It's worth pointing out what the fundamental problem is here, namely: a giant, flagrant violation of the "don't repeat yourself" principle. We have a library function, printf, which at run time is going to parse its format string and try to print some stuff. And then we have a completely different piece of code in the C compiler, which is going to parse the same format string at compile time, and warn the user about potential problems.

With MinGW under Windows the problem is compounded by the fact that the two parsers are written and maintained by two completely different sets of people with no connection whatsoever between them. (Under Linux, at least, there's plenty of coordination between the folks who work on gcc and the folks who work on glibc.)

So it's not too surprising that there's not perfect agreement between the two parsers as to what's accepted. (Indeed it would be a miracle if there were perfect agreement.) The answers to this question describe at some length how imperfect the coordination was in 2012, and here we are 10 years later and it's still not perfect.

Imperfection being the name of the game, there may not be a perfect solution. I have to believe that, in 2021, Microsoft's C run-time library does support %zu. But if the latest version of gcc built into MinGW hasn't caught up to this fact, if it is still warning that the z modifier is unsupported, there may not be much you can do. You can send a bug report to the MinGW folks, and they may act on it eventually, but meanwhile you've got code you need to compile today. You could turn off -Wformat, but you probably don't want to (as indeed I wouldn't), because you probably want -Wformat to keep checking for other mistakes you might make.

I'm part of a group that maintains a large, mature, cross-platform codebase, and although it's an ideal solution that I'd desperately like to use, we still don't routinely use %zu, because we still can't be sure that every compiler and every run-time library we use agrees on it.

I'm not saying this to suggest that you abandon %zu also, but merely to express my sympathy: it's still absurdly difficult, even in 2021, to printf size_t values safely and portably.

printf and %llx in GCC under Windows 64x

As I'm guessing you probably already know, from http://comments.gmane.org/gmane.comp.gnu.mingw.w64.general/4670 (note: dead link; see the Internet Archive's copy),

the issue is that formatter-width specifier %ll isn't supported for
all msvcrt-DLL versions, therefore gcc warns about its use. The
variant for specifying 64-bit integer-scalar-width in formatter for
msvcrt in a backward-compatible way is by using %I64.

Is the %zu specifier required for printf?

If size_t exists shouldn't zu also be available in printf?

size_t existed at least since C89 but the respective format specifier %zu (specifically the length modifier z) was added to the standard only since C99.

So, if you can't use C99 (or C11) and had to print size_t in C89, you just have to fallback to other existing types, such as:

printf("%lu\n", (unsigned long)n);

My code runs smoothly to the end in Linux but ends abruptly in Windows

First error:

You are allocating memory for index and input[i], but not for input itself. This means that you are dereferencing an uninitialized pointer on the following line:

input[i]=(char*)malloc(j*sizeof(char));

This will invoke undefined behavior, which explains why it crashes on that line on one platform, but works on another platform.

Second error:

The line

input[i]=(char*)malloc(j*sizeof(char));

will not allocate sufficient space for storing the string. Since j is the size of the string without the terminating null character, you must allocate j+1 bytes instead.

Due to not allocating a sufficient number of bytes for the string, the following loop will access the memory buffer input[i] out of bounds:

for(k=0;k<j+1;++k){
input[i][k]=buffer[k];
}

This will invoke undefined behavior.

Third error:

Another source of undefined behavior in your program is the following line:

scanf("%ld", &n);

The correct conversion format specifier for size_t is %zu, not %ld. See the documentation of the function scanf for further information.

On 64-bit Microsoft Windows, a long has a width of 4 bytes, but a size_t has a width of 8 bytes. Therefore, because you are using the format specifier for long instead of size_t, the function scanf is probably only writing to half of the variable n, leaving the other half of the variable uninitialized. This is likely to cause trouble when you read the value of n later in the program.

Most compilers will warn you about using the wrong scanf conversion format specifiers, assuming that you enable all compiler warnings. You may want to read this:

Why should I always enable compiler warnings?

Correct printf format specifier for size_t: %zu or %Iu?

MS Visual Studio didn't support %zu printf specifier before VS2013. Starting from VS2013 (e.g. _MSC_VER >= 1800) %zu is available.

As an alternative, for previous versions of Visual Studio if you are printing small values (like number of elements from std containers) you can simply cast to an int and use %d:

printf("count: %d\n", (int)str.size()); // less digital ink spent
// or:
printf("count: %u\n", (unsigned)str.size());

%ld format conversion for portability

I think your least-bad available option is to borrow conceptually from <inttypes.h>:

#ifdef _LP64
#define PRIdword "d"
#define PRIudword "u"
#else
#define PRIdword "ld"
#define PRIudword "lu"
#endif

and then

DWORD data;
printf("%"PRIdword, data);

This makes use of string constant concatenation, which all C90-compliant compilers should support. Note that the correct macro to test is _LP64, not __x86_64__; that way it will Just Work when you go to port to some other LP64 system, or to the shiny new "x32" mode of Linux/x86-64 (32-bit pointers, wide registers).

It might not be a bad idea to invest in a wholesale conversion to the <stdint.h> types, but that won't get you out of this sort of thing, you'd just be writing

int32_t data;
printf("%"PRId32, data);

instead, and as far as I know most Windows compilers still don't have <inttypes.h>, sigh.

In case you're wondering, the % is not inside the macro so you can put format adjusters in if you want:

printf("%-32"PRIdword, data);

MinGW GCC: Unknown conversion type character 'h' (snprintf)

Historically, MinGW has been in a bit of an odd situation, especially as far as C99 support goes. MinGW relies mostly on the msvcrt.dll runtime that's distributed with Windows, and that runtime doesn't support C99.

So with older versions of MinGW, you can run into problems in C99 mode when using C99-specific format specifiers. Also historically, GCC didn't make any special accommodations for msvcrt.dll's lack of support for C99 specifiers. So you'd get into situations where -Wformat wouldn't warn about a format that wouldn't work.

Things are improving on both sides - GCC has specific support for -Wformat when used with the MS runtime, such as:

  • -Wpedantic-ms-format so that GCC won't complain about "I32" and "I64" (even though it's documented, I still get a complaint about it being unrecognized even in 4.7.0 - maybe it's brand new)
  • the ms_printf option to __attribute__((__format__))

On the other side, MinGW has provided its own snprintf() for a while, since MSVC's variant, _snprintf(), behaves quite differently. However, MinGW relied for a long while on the printf() in msvcrt.dll, so C99 format specifiers for printf() didn't work. At some point MinGW started providing it's own version of printf() and friends so that you could get proper C99 (and GNU?) support. However, it seems that to be on the conservative side, these didn't replace the msvcrt.dll versions initially. They have names like __mingw_printf().

It looks like at some point between 4.6.1 and 4.7.0, the MinGW headers started using the MinGW supplied versions as replacements for the msvcrt.dll function (at least if you've specifed C99).

However, it seems that with the newer versions, GCC and MinGW are still a little out of sync. Where as before GCC would not warn about specifiers that wouldn't actually work on MinGW, not it complains about spcifiers that will.

You may want to try the following snipet of code to see how well your version of MinGW support "hhX":

printf("%hhX\n", 0x11223344);
__mingw_printf("%hhX\n", 0x11223344);

I'm not sure what to suggest to fix the problem you're running into - I think that you may be able to patch the MinGW stdio.h header so that it has a __attribute__((__format__ (gnu_printf, ...))) attribute on the printf functions (they're not there in the newer stdio.h, so GCC will use it's default idea of what the format support is).



Related Topics



Leave a reply



Submit