37

Simple program I made when I was experimenting with inttypes.h:

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

bool get_bit(uint32_t x, uint8_t n) {
    x >>= n;
    return x & 1;
}

int main() {
    uint32_t x;
    uint8_t n;

    printf ("Enter x: ");
    scanf("%"SCNu32, &x);

    printf ("Enter n: ");
    scanf("%"SCNu8, &n);

    printf("The %"PRIu8"th bit of %"PRIu32" is: %d", n, x, get_bit(x, n));
    return 0;
}

On my phone (64 bit octa core ARN LTE Soc Android 10) it works fine:

Enter x: 1
Enter n: 0
The 0th bit of 1 is: 1

But on my computer (64 bit x86 Windows 10) I get:

Enter x: 1
Enter n: 0
The 0th bit of 0 is: 0

Changing bool to uint8_t doesn't affect it.

EDIT: I tried compiling with MinGW-w64 GCC C99 and C17.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Clyde B.
  • 399
  • 2
  • 4
  • 6
    Are you sure that your input was successful? Always check the return value of `scanf`. – user694733 Jan 05 '22 at 13:49
  • 3
    I was sceptical too until I did manage to reproduce it. It has to be a compiler infected by the Microsoft CRT or you won't reproduce it. – Lundin Jan 05 '22 at 13:51

1 Answers1

40

It seems you might get this problem in case you are using a Windows compiler which uses Microsoft's non-compliant CRT (non)standard library. That is: Visual Studio or Mingw64/gcc.

I can reproduce it on Mingw/gcc. It's a well-known problem that the Microsoft CRT is broken and for example doesn't support various format specifiers introduced in C99. It would seem that the problem lies in scanf reading with the wrong format specifier, because when compiling with all warnings enabled, I get:

warning: unknown conversion type character 'h' in format [-Wformat=]

%hh being what sits underneath the hood of SCNu8 but the compiler only reads as far as %h and stops there. The scanf call actually fails.

You can uncrap the CRT lib at least in Mingw by using the following:

#define __USE_MINGW_ANSI_STDIO 1
#include <stdio.h>

When I added the above to your code, I get The 0th bit of 1 is: 1.
Without the above patch it I get The 0th bit of 0 is: 0

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 4
    @ClydeB. It's actually not the compiler but the standard lib that's the problem. You can compile this with same gcc version in Linux and Mingw/Windows and get different results. It's the same compiler but they are using different standard libs. – Lundin Jan 05 '22 at 14:42
  • Were you able to reproduce with MSVC? I was unable to with MSVC 2015 on Windows 10. – dbush Jan 05 '22 at 15:04
  • @dbush No I only tried with an old version of Mingw 64. From what I hear various versions of MSVC from 2015 to 2019 have gotten sporadic support for some C99 features. – Lundin Jan 05 '22 at 15:09
  • 1
    If you cannot verify that Microsoft's CRT is indeed broken it would certainly be a good idea to remove that from this proposed answer. If you verified this to be broken with MinGW-64 it would certainly make for a more complete answer if you noted this, including the version you used to verify. – IInspectable Jan 05 '22 at 16:43
  • 6
    @IInspectable This non-conformance is well-known and documented, we have multiple posts about it on the site [example](https://stackoverflow.com/questions/44382862/how-to-printf-a-size-t-without-warning-in-mingw-w64-gcc-7-1). It isn't obvious why the OP's conforming code would go nuts in Windows, so if I hadn't known of this flaw in the MS CRT, I would never have been able to answer. `__USE_MINGW_ANSI_STDIO` wasn't just random letters I typed by accident but a define which calls upon a bug fix for this non-conformance issue. If the CRT was conforming then why did someone write a bug fix... – Lundin Jan 05 '22 at 17:15
  • 9
    MSVC obviously doesn't support C99 (and doesn't claim to do). Though, just because of that, claiming (or at least strongly suggesting) that the issue were present in any recent Microsoft compiler is just wrong. As for the bug fix: That's clearly in MinGW. MinGW is known to be **hopelessly** outdated. As far as I can see: This bug isn't present in any Microsoft CRT shipped in the past 6 years. Anyway, the down-vote is for misinformation. – IInspectable Jan 05 '22 at 17:33
  • 1
    @IInspectable MSVC do claim as much, [source](https://learn.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)?redirectedfrom=MSDN). "**C99 Conformance** Visual Studio 2015 fully implements the C99 Standard Library, with the exception of any library features that depend on compiler features not yet supported by the Visual C++ compiler (for example, is not implemented)." As for which version of the CRT that Mingw64 uses, I don't know. This version I have installed is some 5 years old or so. – Lundin Jan 05 '22 at 17:45
  • 8
    So your line of reasoning is that some version of Visual Studio (which incidentally didn't promise to implement the C99 Standard Library) sucks, because it doesn't implement the C99 Standard Library. And because of that, all future versions of Visual Studio suck (including 2015+ that promise to implement the C99 Standard Library and deliver on that promise). In doing so you misrepresent that the bug is in MinGW, which uses Microsoft's libraries, **out of spec**, no less. But the answer still reads: *"It's a well-known problem that the Microsoft CRT is broken"*. – IInspectable Jan 05 '22 at 19:33
  • 5
    @IInspectable Microsoft wrote the CRT, the maintainers of Mingw did not. Again, I don't know which version of the CRT it uses but the fact remains that it 1) was written by Microsoft and 2) it contains bugs. No matter what compiler or standard lib/CRT that is used, using older versions with known bugs isn't advisable. And as it happens the root of all these problems is Microsoft's persistent, arrogant refusal to follow technical ISO standards including ISO 9899:1999. Everyone else in the industry but them do. That's how we end up with bugs like these. – Lundin Jan 06 '22 at 10:04
  • 5
    The C runtime is system-specific, and is therefore supposed to come with the system. But since Microsoft stopped preinstalling newer versions of the dynamic library in Windows long ago (I think last one is 6.0 from 1998), MinGW defaults to that ancient version for compatibility so you can use dynamic libraries and don't have to create installer with the redistributable package installing the newer runtime. It has an option to choose any available version. – Jan Hudec Jan 06 '22 at 10:20
  • @jan The CRT is a **language** support library. It should be part of the language implementation (i.e. the C compiler, MinGW in this case), not the OS. Regardless, the [Universal CRT](https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment) is now an operating system component, and has been for years. MinGW could well have taken advantage of this, but didn't. – IInspectable Jan 06 '22 at 14:25
  • @IInspectable: Certain corner cases in functions like `scanf` have different defined behaviors in C89 and C99. Consequently, a C implementation shouldn't attempt to use external OS-supplied functions but should instead bundle its own. On the flip side, if a program only uses a subset of the Standard's defined functionality, an implementation that bundles a smaller version of such functions may be more useful than one that bundles more bloated ones. – supercat Jan 06 '22 at 19:23