3

I was doing problem 2.1 of K&R C which basically wants us to mess around and understand the header library

#include<stdio.h>
#include<limits.h>
#include<float.h>

int main()
{
    printf("%lu", ULONG_MAX);
}

When I run this program, I get the output

4294967295

which is equal to 232 -1. I was expecting this value (since K&R has the same value in it's appendix).

Now I changed %lu to %llu.

With my little knowledge of the C language, I am assuming that %llu is a bigger 'placeholder' for the value of ULONG_MAX.

I expected to get the same output, but instead I got a vague output

38654705663

Isn't ULONG_MAX supposed to be a constant? Why is it changing? Also, in quest for an answer, I stumbled upon this.

I understand the argument that the standard mentions the term 'minimum' but when I tested this with CHAR_MAX (i.e. tried to print CHAR_MAX with a %llu specifier), I got

38654705663

This is contradictory to everything I have ever read about char in C.

Really hope that someone clears the confusion regarding this.

ssell
  • 6,429
  • 2
  • 34
  • 49
Karan Singh
  • 1,114
  • 1
  • 13
  • 30

3 Answers3

5

As printf is expecting now to print a 64 bit value, you should pass it accordingly. You can cast it to unsigned long long:

    printf("%llu", (unsigned long long)ULONG_MAX);

Assuming a little endian architecture, you got that bigger value previously because only the least significant 32-bit word was passed as a parameter, and printf needed 64 bit, so it was using as the most significant 32-bit word whatever the stack contained at the next address.

The wrong value you got is 38654705663. If you display it in hexadecimal (you can also use printf for this with %llx), you get: 0000 0008 FFFF FFFF (spaces added by me for easier reading).

nnn
  • 3,980
  • 1
  • 13
  • 17
  • 1
    Thanks for explaining _why_ they were seeing different values between using `lu` and `llu`. So the upper 32-bit word of `0000 0000 0000 0000 0000 0000 0000 1000` was just whatever garbage was laying around? – ssell Mar 15 '17 at 20:43
  • Yes, I just added in the answer the hex representation of the wrong value. – nnn Mar 15 '17 at 20:55
4

Types of additional printf() parameters must match used placeholders. Otherwise the behaviour is undefined.

%llu stands for unsigned long long, thus your parameter also has to be unsigned long long:

printf("%llu", (unsigned long long)ULONG_MAX);
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 1
    Isn't it a constant, so why does it display some other value? Should it not overflow? – Karan Singh Mar 15 '17 at 20:34
  • 1
    @KaranSingh There is a thing called *"undefined behaviour"*. It means that your program is not guaranteed to function properly anymore, and anything can happen with it. This term is used a lot in the standard in phrases like *"If you , then the behaviour is undefined"*. – HolyBlackCat Mar 15 '17 at 20:37
  • 1
    Simply saying, what happens is that `printf` expects to get a 64-bit wide unsigned integer (`unsigned long long` is *usually* 64 bits wide), but you give it a 32-bit wide integer. Because of that it (probably) reads not only your parameter, but also some nearby bytes. – HolyBlackCat Mar 15 '17 at 20:40
  • 2
    @KaranSingh: The variadic C functions like `printf` rely on the format string to know the size of the argument to pull from the `va_list`. If you give `printf` a format string that requires 8 bytes, but only provide 4 bytes on the argument list, things go very badly. – Zan Lynx Mar 15 '17 at 20:41
  • 2
    Wow, I get it now. It would be great if you added that to your answer. Also, I tried doing it with CHAR_MAX and now I get 127 :) Thanks a lot. – Karan Singh Mar 15 '17 at 20:42
2

With %llu you have to pass a long long integer to the printf(). Try this:

printf("%llu", (unsigned long long)ULONG_MAX);
Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50