-4

If I build and run the following program in Visual C++ 2017:

#include <stdio.h>

int main()
{
    int a[3] = { 0 };

    for (int i = 0; i < 3; i++)
    {
        printf("%llu %u %p\n", a + i, a + i, a + i);
    }

    return 0;
}

I see output like as follows:

31519768560270096 7338768 000F1055
31519785740139284 7338772 000F1055
31519802920008472 7338776 000F1055

That I cannot relate.

Why the output of %llu are so different? sizeof(int) on my platform is 4.

Why the output of %p are all same? They are address of different variables.

Only the output of %u seems to be consistent - consecutive memory location of 3 array elements each having 4 bytes. But these output matches with neither %llu nor %p.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Donotalo
  • 12,748
  • 25
  • 83
  • 121
  • 1
    Have you thought about reading the manual page for `printf` - i.e. [here](https://linux.die.net/man/3/printf) – Ed Heal Jan 16 '18 at 20:57
  • 3
    a lot of UB here.* – Jean-François Fabre Jan 16 '18 at 20:58
  • I would say that `%llu` consumes too much bytes, which "shifts" further readings. which probably explains why the last value is the same: it's reading arguments out of the argument area. – Jean-François Fabre Jan 16 '18 at 21:01
  • Using any conversion specifier other than `%u` to print pointer values leads to undefined behavior. Replace that call with `printf( "%p\n", a + i );` and see if that doesn't give you the results you expect. – John Bode Jan 16 '18 at 21:02
  • 2
    Consider using a compiler that will warn you if your `printf` arguments do not match the expected types in the format specifiers. – jxh Jan 16 '18 at 21:10

4 Answers4

5

Your code causes undefined behaviour by using the wrong format specifiers. The results are meaningless.

It is up to the programmer to use the correct format specifier for the argument provided, there is no intermediate "compiler magic" in C. If you don't follow the rules then your program is no longer covered by any of said rules and anything can happen, which in your case manifested itself as unexpected output.

M.M
  • 138,810
  • 21
  • 208
  • 365
4

Oups! You are trying to print with %llu something that is not a long long. This is enough to invoke Undefined Behaviour, and you can no longer expect that your program gives correct output.

Under the hood, common compilers implementation push parameters on the stack, and the printf function gets them from there. On a 32 bit architecture, pointers use only 4 bytes, so your call actually pushes 12 bytes onto the stack.

But the %llu uses 8 bytes, eating 2 of the passed parameters. So the %u correctly displays last one, and %p prints what comes after in the stack. It begins even more evident if you convert the long long unsigned into hexadecimal: 31519768560270096 is 0x006FFB10006FFB10, which is in fact the 2 initial parameters having 0x6FFB10 or in decimal 7338768

TL/DR: NEVER use anything but %p to print addresses, unless you really know why (and even in that case, try to refrain...)

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks for the explanation. I was just playing with pointers and came up with the code above. I didn't know that `%llu` would consume 8 bytes. But not knowing it causes several negative votes. :) Anyway, it's a good lesson. – Donotalo Jan 16 '18 at 21:24
  • @Donotalo: Beware, that behaviour is not mandated per standard and is no more than the one of a specific compiler with specific options. That's why I said ensure that the printf formatter and the value to print agree on the type... – Serge Ballesta Jan 16 '18 at 21:27
3

let's dump the first argument in hex:

>>> hex(31519768560270096 )
'0x6ffb10006ffb10'  <=== twice the same 0x6ffb10 32-bit value
>>> hex(31519785740139284 )
'0x6ffb14006ffb14'  <=== same here, increased by 4

as you see, using the first argument wrong format specifier makes printf read too much from the variable argument area, so the second argument is actually the third argument (they have the same value), but in the end the last argument (the pointer) reads outside the variable argument area. That's why it's not varying

Anyway, that's just to explain what happens behind the scenes for your particular compiler implementation, but don't do that ever because it's undefined behaviour.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
2

Use matching types for each printf specifier. As OP's code does not do this,the result is undefined behavior (UB). Explaining UB is often not nearly so valuable as learning how to avoid it.

@jxh advice is sound, use a better compiler or options.

printf("%llu %u %p\n",
   // a + i, a + i, a + i);
   (unsigned long long) (a + i), (unsigned) (a + i), (void*) (a + i));

This may not print the same text for the 3 (a+i). I would expect the %u to be a truncated value, but at least these is no UB, just implementation defined behavior.

Use the last form for the best result when printing the value of object pointers.

printf("%p\n", (void*) (a + i));
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256