1

I found this on Win32 docs:

// Check the smart card context handle.
// hContext was set previously by SCardEstablishContext.

LONG    lReturn;
lReturn = SCardIsValidContext(hContext);
if ( SCARD_S_SUCCESS != lReturn )
{
   // Function failed; check return value.
   if ( ERROR_INVALID_HANDLE == lReturn )
       printf("Handle is invalid\n");
   else
   {
       // Some unexpected error occurred; report and bail out.
       printf("Failed SCardIsValidContext - %x\n", lReturn);
       exit(1);  // Or other appropriate error action.
   }
}
else
{
   // Handle is valid; proceed as needed.
   // ...
}

The printf("Failed SCardIsValidContext - %x\n", lReturn); line is passing an argument of type LONG (typedef for long) to printf where printf expects unsigned int according to cppreference.com. Is this well defined behavior? If so, is it the same as an explicit static_cast to unsigned int?

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93

2 Answers2

2

is an argument of type long OK for %x printf specifier?

No, it is not OK in C++. %x is for unsigned int, not for long.

Is this well defined behavior?

No, this is undefined behaviour. Quote from C standard draft (which is where the format specifiers are defined):

x,X The unsigned int argument is converted to ... unsigned hexadecimal notation ...

If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.


It may be "OK" in windows if the system documents so, but relying on such guarantee will not be portable to other systems. I suspect that the example is not intended as documentation of validity of such misuse of format specifier, but that it is accidental.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • This is the correct answer by the letter of the standard (+1), though that's essentially saying that `int n; unsigned k = *(unsigned *)&n;` is UB, which I find a bit surprising. – dxiv Aug 17 '20 at 18:07
  • 1
    @dxiv It says nothing about reinterpreting signed integer as its unsigned counterpart, which is well defined. – eerorika Aug 17 '20 at 18:13
  • Right, but `printf` presumably does something like `unsigned k = va_arg(args, unsigned)` when it hits `%x`, and all implementations that I know of translate that to `k = *(unsigned *)(void *)args`. So in the end, the UB is reading an `unsigned` from the address where an `int` was written. – dxiv Aug 17 '20 at 18:25
1

printf("%x", (LONG)101); is valid and well defined in Windows where LONG is defined to be 'a 32-bit signed integer", same as int and same size as unsigned int.

The portable equivalent using C++ types would be printf("%lx", (unsigned long)101l);

dxiv
  • 16,984
  • 2
  • 27
  • 49
  • Is `%x` valid, or `%lx` or both? – Aykhan Hagverdili Aug 17 '20 at 16:30
  • @Ayxan Both, given how Windows defines the type, which is not expected to change anytime soon since it would break the entire Win32 API. – dxiv Aug 17 '20 at 16:41
  • 1
    Interesting. I was asking this because cppreference says *"If any argument after [default conversions](https://en.cppreference.com/w/cpp/language/variadic_arguments#Default_conversions) is not the type expected by the corresponding conversion specifier, or if there are fewer arguments than required by format, the behavior is undefined."* While the size matches, the type still doesn't. So, I am not sure. Perhaps I am missing something. – Aykhan Hagverdili Aug 17 '20 at 17:34
  • @Ayxan Using `"%x"` with a `signed int` in `printf` is indeed UB, as elaborated in [this answer](https://stackoverflow.com/a/32352833) (for C, which the Win32 API is using). I am not aware of any Windows compilers which do *not* do the expected with such construct, but technically it's still a language violation. I guess something like `printf("%x", lReturn + 0ul);` would work around that. – dxiv Aug 17 '20 at 18:00