-1

I want to know why sizeof doesn't work with different types of format specifiers.

I know that sizeof is usually used with the %zu format specifier, but I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf

int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%d\n", sizeof(a + d));  // prints normal size of expression
printf("%lf\n", sizeof(s));     // prints big number
printf("%f", sizeof(d));        // prints nan
chqrlie
  • 114,102
  • 10
  • 108
  • 170
  • 3
    Try number `5` instead of `sizeof`. You will end up with a similar but simplified question. – Eugene Sh. Oct 23 '19 at 18:35
  • 1
    The return type of `sizeof` is always the same, it doesn't change – UnholySheep Oct 23 '19 at 18:35
  • See https://stackoverflow.com/questions/27296011/correct-format-specifier-for-return-value-of-sizeof-in-c – Andrew Henle Oct 23 '19 at 18:36
  • I'll let someone else pick the appropriate dupe.... – Andrew Henle Oct 23 '19 at 18:36
  • `sizeof is usually used with the %zu` - no, sizeof should only be used with `%zu`. It's not "usually", it's "can only be". – KamilCuk Oct 23 '19 at 18:42
  • @KamilCuk Except the common case of M$ who do not support `%zu`.... – Eugene Sh. Oct 23 '19 at 18:44
  • @KamilCuk ok, but still what happens when i use %f? Why it prints out as nan? – Mihalache Rares Oct 23 '19 at 18:51
  • Because it is *undefined behavior*. You are telling the `printf` that you are giving it `float` while actually giving it `size_t`. These two are represented differently in memory. – Eugene Sh. Oct 23 '19 at 18:53
  • @EugeneSh. what is M$, sir ? – snr Oct 23 '19 at 19:03
  • This has nothing to do with the `sizeof` operator, and everything with `printf()`'s varargs nature. – wildplasser Oct 23 '19 at 19:03
  • @snr Well, urban jargon for MS AKA Microsoft :) – Eugene Sh. Oct 23 '19 at 19:06
  • @EugeneSh. you are carrying your loaded opinion which is incorrect. MSVC supports `"%zu"`. – Weather Vane Oct 23 '19 at 19:31
  • @WeatherVane mingw gcc with microsoft C runtime library [is not supporting it](https://stackoverflow.com/questions/44382862/how-to-printf-a-size-t-without-warning-in-mingw-w64-gcc-7-1). It is not "loaded opinion" but a personal experience. – Eugene Sh. Oct 23 '19 at 19:45
  • @EugeneSh. `"%zu"` in MSVC is documented on [this page](https://docs.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=vs-2017) and confirmed in the question you linked - since 2013. Perhaps MS did not support it as soon as others did. Telling the OP that "M$ do not support %zu" is misleading, and your earlier comment came across as yet another tedious pop at MS. – Weather Vane Oct 23 '19 at 20:40
  • @EugeneSh. thx sir (: – snr Oct 24 '19 at 05:09

2 Answers2

4

sizeof evaluates to a value of type size_t. The proper specifier for size_t in C99 is %zu. You can use %u on systems where size_t and unsigned int are the same type or at least have the same size and representation. On 64-bit systems, size_t values have 64 bits and therefore are larger than 32-bit ints. On 64-bit linux and OS/X, this type is defined as unsigned long and on 64-bit Windows as unsigned long long, hence using %lu or %llu on these systems is fine too.

Passing a size_t for an incompatible conversion specification has undefined behavior:

  • the program could crash (and it probably will if you use %s)
  • the program could display the expected value (as it might for %d)
  • the program could produce weird output such as nan for %f or something else...

The reason for this is integers and floating point values are passed in different ways to printf and they have a different representation. Passing an integer where printf expects a double will let printf retrieve the floating point value from registers or memory locations that have random contents. In your case, the floating point register just happens to contain a nan value, but it might contain a different value elsewhere in the program or at a later time, nothing can be expected, the behavior is undefined.

Some legacy systems do not support %zu, notably C runtimes by Microsoft. On these systems, you can use %u or %lu and use a cast to convert the size_t to an unsigned or an unsigned long:

int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%u\n", (unsigned)sizeof(a + d));       // should print 8
printf("%lu\n", (unsigned long)sizeof(s));     // should print 4
printf("%llu\n", (unsigned long long)sizeof(d)); // prints 4 or 8 depending on the system
chqrlie
  • 114,102
  • 10
  • 108
  • 170
1

I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf

Several reasons.

First of all, printf doesn't know the types of the additional arguments you actually pass to it. It's relying on the format string to tell it the number and types of additional arguments to expect. If you pass a size_t as an additional argument, but tell printf to expect a float, then printf will interpret the bit pattern of the additional argument as a float, not a size_t. Integer and floating point types have radically different representations, so you'll get values you don't expect (including NaN).

Secondly, different types have different sizes. If you pass a 16-bit short as an argument, but tell printf to expect a 64-bit double with %f, then printf is going to look at the extra bytes immediately following that argument. It's not guaranteed that size_t and double have the same sizes, so printf may either be ignoring part of the actual value, or using bytes from memory that isn't part of the value.

Finally, it depends on how arguments are being passed. Some architectures use registers to pass arguments (at least for the first few arguments) rather than the stack, and different registers are used for floats vs. integers, so if you pass an integer and tell it to expect a double with %f, printf may look in the wrong place altogether and print something completely random.

printf is not smart. It relies on you to use the correct conversion specifier for the type of the argument you want to pass.

John Bode
  • 113,266
  • 18
  • 112
  • 190