2

There is another question that discusses something like this: When printf is an address of a variable, why use void*?, but it only answers why shouldn't you print pointers as ints.

Another question discusses you should always cast pointers to void* when passing them to variadic functions: Argument conversion: (normal) pointer to void pointer, cast needed?. It says if you don't do so you invoke undefined behavior, but it doesn't go beyond that.

Indeed:

      if (pIReport4 == NULL)
      {
          printf("It's NULL but when I print it, it becomes: %p\n", pIReport4);
          printf("It's NULL but when I print it and cast it into (void*), it becomes: %p\n", (void*)pIReport4);
          printf("And NULL is: %p\n", NULL);
      }

Prints:

It's NULL but when I print it, it becomes: 0xc68fd0
It's NULL but when I print it and cast it into (void*), it becomes: (nil)
And NULL is: (nil)

pIReport4 is a non-void pointer.

It's clear it's pushing something else into the stack if you don't do the cast. What might it push? Why?

What's the rationale of making passing non-void pointers undefined behavior? It doesn't make sense to me...

I always thought pointer casting is just a hint to compiler how to interpret the pointed data when reading or writing it. But when passing just the pointer value I would expect that it passes the same sequence of bytes regardless of type.

Community
  • 1
  • 1
Calmarius
  • 18,570
  • 18
  • 110
  • 157

2 Answers2

4

As the answer in the second link explains that

For printf, p conversion specifier requires a void * argument. If the argument is of a different type, the function call invokes undefined behavior. So if the argument of pis an object pointer type, the (void *) cast is required.

That is, since your code snippet invokes undefined behavior, you can get anything, either expected or unexpected result. The result you are getting may also vary compiler to compiler. On my compiler (GCC 4.8.1) it is giving the result:
enter image description here

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 3
    What's the rationale of making this undefined behavior? – Calmarius Jan 08 '14 at 14:29
  • 2
    @Calmarius: In C, there is no runtime information about types; in case of a variadic function there is neither at compile time—but what I'm curious about is why default argument promotions don't include object pointers being converted to `void *`, in which case we got rid of most of the casts. – mafso Jan 08 '14 at 14:37
  • 1
    @mafso But what's the difference between void* and nonvoid* when I consider the pointer value only? Also the static types are known at the call site of the variadic function. – Calmarius Jan 08 '14 at 14:47
  • @Calmarius; `void *` type can point to any type (default conversion) but non-`void *` type can point only to a specific type (that non-`void` type). – haccks Jan 08 '14 at 14:51
  • 4
    The bit patterns for `void *` and `char *` must be the same (as I understand the standard). However, the 'anything else' pointer to an address does not have to be the same as the `char *` address to the same memory location. I learned C on a word-oriented machine (ICL Perq), where the `char *` value for an address was different from the `int *` value for the same address. You got to be very careful about (a) ensuring `malloc()` was declared (returning `char *` in those days; `void` and `void *` were not yet around), and (b) you got used to casting the result to the correct type. […continued…] – Jonathan Leffler Jan 08 '14 at 15:18
  • 2
    […continuation…] I believe the standard takes the position it does so that machines such as that can comply with the standard. Some mainframe computers also have interesting address structures. – Jonathan Leffler Jan 08 '14 at 15:19
  • @JonathanLeffler; But now a days no need to cast the return value of `malloc`. – haccks Jan 08 '14 at 17:21
  • @JonathanLeffler: All variations of char* and void* are compatible. All variations of struct* are compatible (with each other). All variations of union*. All variations of short*, of int*, of long*, of long long*, including unsigned variants (reading the wrong signedness is allowed if reading a positive signed int, or an unsigned int that fits in the signed type). – gnasher729 Feb 15 '16 at 19:51
  • @haccks: You don't need to _cast_ the result of malloc, but there wlll be a _conversion_ converting void* to the right type. – gnasher729 Feb 15 '16 at 19:52
0

By default all arguments to variadic functions are passed as the type of the variable passed.

You did not mention what type pIReport4 is, but assuming it's for example an int then it will be passed as 4 bytes on the stack. If you happen to be on a 64-bit system then sizeof(void *) is 8 bytes. Thus printf will read 8 bytes from the stack and there is your undefined behaviour.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sergey L.
  • 21,822
  • 5
  • 49
  • 75
  • "By default all arguments to variadic functions are passed as the type of the variable passed." - no, there are default promotion rules. –  Jan 08 '14 at 17:36