-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
  • 131,814
  • 10
  • 121
  • 189

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
  • 131,814
  • 10
  • 121
  • 189
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
  • 119,563
  • 19
  • 122
  • 198