13

I have this piece of code that is challenging all my knowledge of C. Here I have :

int main(void){
    unsigned long long int massage ;

    scanf("%llX", &massage); //input: 0x1234567890abcdef
    printf("%llX", massage);
    return 0;
}

On my "64bit - Corei5 - Fedora - GCC" it prints out exactly what I fed it. but on my buddy's system (32bit, MS XP, MinGW) it prints 90ABCDEF. I don't understand why. does anyone know?

BTW: sizeof(unsigned long long int) on his system is 8.

Untitled
  • 761
  • 1
  • 6
  • 24
  • 3
    He's using MSVC or so? They have non-standard `printf` and `scanf` formats. Should be findable in the documentation of the compiler. – Daniel Fischer Dec 28 '12 at 15:24
  • he uses MINGW. (editted the question) – Untitled Dec 28 '12 at 15:25
  • 2
    Does MinGW use its own C library, or does it use the Windows one? In the latter case, the non-standard formats still apply. In the former, no idea. – Daniel Fischer Dec 28 '12 at 15:28
  • 1
    @DanielFischer Related: http://stackoverflow.com/questions/13590735/printf-long-long-int-in-c-with-gcc#13590809 Apparently, it uses Windows' one. –  Dec 28 '12 at 15:36

2 Answers2

10

The issue is a discrepancy between what the compiler believes (as reflected in sizeof: sizeof(unsigned long long int) is evaluated at compile-time) and what the run-time library believes (as reflected in printf: the printf function is called at run-time, so that's when its format-specifiers take effect).

According to "C99" in the MinGW documentation:

GCC does not include a C runtime library. This is supplied by the platform. The MinGW port of GCC uses Microsoft's original (old) Visual C runtime, MSVCRT, which was targeted by Microsoft Visual Studio 6 (released in 1998).

[…]

Because MinGW relies on MSVCRT, it has many of the same limitations and quirks with compatibility as Visual Studio 6. You should assume that MinGW applications cannot rely on C99 behaviour, only on C89. For example, the newer format characters in printf like %a and %ll are not supported, although there exists a workaround for %ll.

(The workaround that it mentions is to use I64 instead of ll: so, %I64X. Annoyingly, at least on my system, GCC will issue a warning when it sees that in a literal format-string, because it assumes it'll have a better run-time library.)

Community
  • 1
  • 1
ruakh
  • 156,364
  • 23
  • 244
  • 282
  • You could use preprocessor string concatenation to get around the GCC warning. It won't make your code nicer to look at, but at least it will use the right code for the right library. –  Dec 28 '12 at 16:02
  • @Tinctorius: It's not that it's hard to work around the warning -- for example, you can just store the format-string in a temporary variable -- it's just that it's annoying to have to. :-P – ruakh Dec 28 '12 at 16:12
  • 1
    It's still nice to have your compiler check your format string. After all, the correctness of the format string depends on the other arguments to `printf` and `scanf`, so in a way, it's part of the function. You could create the preprocessor constant `HEX64` instead, which translates to `"%I64X"` for MSVCRT builds, and `"%llX"` for POSIX-like builds. Then you can write `printf("I like the number " HEX64 "!", number)`, and GCC can still check if the type of `number` agrees with the format string. –  Dec 28 '12 at 16:36
  • 1
    @Tinctorius: Sorry, I think we're miscommunicating. What I mean is -- when I'm on a Windows system with MinGW, and I use a literal string with `%I64X`, GCC will give me a warning-message, because it doesn't recognize that notation. It simply doesn't know anything about the runtime library, so it assumes a POSIX-y one. (That is, GCC's warnings try to get me to use POSIX notations, even when I'm on a system where the POSIX notations will not work. Which makes sense, but is annoying.) – ruakh Dec 28 '12 at 17:05
  • I see. Then it's GCC's "fault" for not making this checking easily extensible. Or actually MSVCRT for being a bad library. –  Dec 28 '12 at 17:36
  • There's no need to worry about this. MinGW provides in its copy of `inttypes.h` a set of Windows-compatible definitions for C99 `printf` type specifiers, so if you use C99-conforming specifiers you'll avoid these issues. Instead of `printf("%llx\n", foo)` just `#include ` and use `printf("%" PRIx64 "\n", foo)`; MinGW will make it `%I64x` and UNIX-like platforms will make it `%llx`. You may need to also pass `--std=gnu99` to GCC. – Jody Bruchon Feb 13 '17 at 17:44
  • @JodyLeeBruchon: Thanks! Do you know when that was added? (That is: how can a person easily tell whether that will work on a given system, short of trying it out?) – ruakh Feb 13 '17 at 19:47
  • It is a part of the C99 standard, so it was added to the standard in 1999. Many compilers adhere to the C89 standard by default (this was changed in GCC to C11 by default fairly recently, I think) but this is not a syntactic or keyword thing, it's a header thing, so it should work on any C99-conforming compiler. `inttypes.h` is provided by all modern systems except MSVC++; all the UNIX-like systems covered by GCC or Clang will have it, as does every fork of MinGW for Windows. For MSVC++ you can use this: https://github.com/mattn/gntp-send/blob/master/include/msinttypes/stdint.h – Jody Bruchon Feb 14 '17 at 12:20
  • Use `-D__USE_MINGW_ANSI_STDIO=1` to use the real stdio, instead of the sh*tty windows one. – Shahbaz Nov 03 '17 at 14:35
4

The Windows C library uses "%I64d", not "%lld", to print arguments of type "long long".

Ref: http://gcc.gnu.org/ml/gcc-patches/2004-11/msg01966.html

Rubens
  • 13,469
  • 10
  • 56
  • 90