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.
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:
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.
Related
For example,
int a = 6;
printf("%p", &a);
this would print the address of the pointer of a right?
but what if we print without &?
int a = 6;
printf("%p", a);
Can someone please tell me what it prints out?
This is undefined behavior, because %p expects a void*
printf("%p", a);
Adding an explicit conversion is allowed, but the behavior is implementation-defined:
printf("%p", (void*)a);
"Implementation-defined" means that the compiler is free to do whatever it wants when converting an int to void*. See this Q&A for exact quotes from the standard.
In many implementations you would see a pointer assigned the numeric value of int (demo 1). Other implementations would warn you that the behavior is meaningless. If you treat warnings as errors, the code would fail to compile (demo 2).
According to the C standard, it's undefined behavior to send wrong matching type argument to printf(). And int != void *, so it's undefined behavior.
Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.
Source
By the way, that why you should write printf("%p", (void *)&a);
warning: format specifies type 'void *' but the argument has type 'int *' [-Wformat-pedantic]
printf("%p", &a);
~~ ^~
The C code is below. I know the code will cause core dump. What is the output we can see? Is it different for different platforms?
#include <stdio.h>
int main(){
printf("abc\n123");
printf("IHJ");
printf("%d", int* 0);
return 0;
}
I know the code will cause core dump
Well, sadly, your information is wrong. The above code will not compile. Read on.
There is an issue in your code with the third printf().
printf("%d", int* 0);
is not valid and it won't compile. %d expects an int variable, not the int keyoword itself.
Even if, that int* is meant to be a cast, like
printf("%d", (int*)0);
it's wrong, also. You'll need %p to print a pointer (address). Supplying invalid type of argument for a particular format specifier invokes undefined behaviour.
FWIW, when you face UB, you might get a segmentation fault and a core-dump.
Otherwise, as long as you supply the required type and number of arguments to the format specifiers supplied with printf(), you'll have a well-defined behaviour.
To be pedantic, int main() should be int main(void)
printf("%d", int* 0);
This will not compile. If you meant
printf("%d", (int*)0);
then it's also wrong (using wrong format specifier is undefined behaviour) since the format specifier for printing a pointer is %p. So you meant
printf("%p", (void*) (int*)0);
Then the resulting representation printed by %p is implementation defined.
C standard states
C11, 7.21.6 Formatted input/output functions
The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner.
As has been mentioned before,
printf( "%d", int* 0 );
will not compile, as int* 0 is not a legal expression.
If written as
printf ( "%d", (int *) 0 );
then the behavior is undefined, since %d expects its corresponding argument to have type int, not int *. This will most likely not cause a core dump, but any output should not be trusted.
If written as
printf( "%p", (int *) 0 );
the the behavior is still undefined, since %p expects a void * (this is one of the few places where you need to explicitly cast a pointer value to void * in C). Again, this will most likely not cause a core dump, but any output should not be trusted.
If written as
printf( "%p", (void *) 0 );
then it should print out the implementation-defined value for a NULL pointer, which may not or may not be 0 (assuming you follow that up with a newline or a call to fflush( stdout ), anyway).
If written as
printf( "%d", *(int *) 0 );
then this will most likely cause a core dump. Strictly speaking, the behavior is still undefined since you are dereferencing an invalid pointer, and there may be systems where doing so won't result in an immediate crash.
If the third printf() had the exact for as shown:
printf("%d", int* 0);
This will result in a compiler error. If, however, you meant
printf("%d", (int *)0);
The there are two problems:
The format specifier is for an integer, not a pointer. If both have the same size, you will luckily get the same output. But it actually is undefined behaviour. Use "%p" for pointers.
Annother problem (and more general) is that you are passing a null pointer. As the whole treatment of pointers is implementation specific, your printf() might handle it differently from valid pointer values.
Either way, it is not guaranted to core dump or show any specific behaviour. It might even show no errorneous behaviour (worst case - you do not notice something went wrong). The essence of "undefined behaviour" is that anything can happen. Your computer might fly away.
OK, I have heard things like you should cast a pointer to a generic one i.e void * before printing it and must practice the use of %p placeholder instead of %d in the printf function for printing them.
I just have a feeling that it might be done to prevent truncation of large addresses while printing or Is it something else? But the point is that if you are on a machine with 64 bit pointers and 32 bit integers; use of %p instead of %d will solely do the job.
Is anyone is aware of any practical situations where this casting technique is helpful?
Because the specification of the %p specifier is that it prints a void *. It doesn't know how to print any other type of pointer.
With printf the caller must convert the argument to the right type ; the printf function cannot perform any conversions because it does not have access to the type information about what arguments you actually passed in. It can only assume that you passed the right ones.
C99 7.19.6.1#7
p The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an implementation-defined
manner.
C99 7.19.6.1#9
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
"Undefined behaviour" means anything can happen. In practice, you probably get away with it if the pointer you pass has the same size and representation as void * does. But you should not rely on undefined behaviour.
On modern systems, all object pointers (i.e. not function pointers) have the same size and representation. If you know you are on such a system, and it is not really important if the program misbehaves, then you probably get away with passing the wrong pointer type.
One place where it would be important is if you try to print a function pointer; as there are modern systems which have function pointers a different size to object pointers. It's not guaranteed that casting a function pointer to void * will be allowed by the compiler, but at least you'll get a compiler error message when you try it.
I have some confusions about what I read from the following site about memcpy()(and malloc()):
http://www.cplusplus.com/reference/cstring/memcpy/
In that page,the following 2 lines are clearly stated:
destination
Pointer to the destination array where the content is to be copied, type-casted to a pointer of type void*.
source
Pointer to the source of data to be copied, type-casted to a pointer of type const void*.
But immediately after that,in the code,there is no casting to void* in the following two lines where memcpy() is used:
memcpy ( person.name, myname, strlen(myname)+1 );
memcpy ( &person_copy, &person, sizeof(person) );
Please answer the following 2 questions arising from this premise:
1) In C's case(as opposed to C++) is it all right and advisable not to cast to void* the return type or the arguments in memcpy() just as it is all right and advisable not to cast to void* the return type of malloc() in C?If so,as I intuitively feel, why is it explicitly stated in that reputed site that we need to cast it to void* (even though it doesn't use that in the code).Is that site wrong about it?
2) Now the real contradiction about that reputed site.Consider the following
http://www.cplusplus.com/reference/cstdlib/malloc/
In the case of malloc() ,in the description, it is written as if it is optional to cast to void* the return type (exact words "..can be cast to the desired type.."),unlike in the case of memcpy() above where it is said that it is to be cast into void*.But while in memcpy() the casting is not done even though it is written that it is to be cast,in the case of malloc(),the casting to void* is done even though it's written it can be cast to void*.Now I see something wrong in this as for C we are not supposed to cast malloc()'s return to void*.
To put the discrepancies in a nutshell again lest the people answering get confused in my wordy description:
--Is it advisable in C not to cast to void* the return and arguments of memcpy()?
--Is that site wrong about malloc() as it casts malloc() return to void* in C code.
From ISO/IEC 9899:2011 of C language specification, section 6.3.2.3, page 55:
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
So you basically never need to cast the result of a void* to the desired type, neither you need to do the opposite.
->for first question
memcpy, the cast is not required in C. It would be in C++.
->for second question
malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. The use of casting is required in C++ due to the strong type system, whereas this is not the case in C. The lack of a specific pointer type returned from malloc is type-unsafe behaviour according to some programmers: malloc allocates based on byte count but not on type. This is different from the C++ new operator that returns a pointer whose type relies on the operand
In C we directly assign void* to any type and vice-versa, we don't have to explicitly typecast.
int *i_ptr = (int *)0xABCD; //some address
printf("address :%p\n",i_ptr);
void *v_ptr = i_ptr; //no need to explictly typecast
printf("address :%p\n",v_ptr);
float *f_ptr = v_ptr; //this will throw error in cpp
printf("address :%p\n",f_ptr);
output:
address :0xabcd
address :0xabcd
address :0xabcd
All these are valid statements in C, but in CPP float *f_ptr = v_ptr will cause error invalid conversion from ‘void*’ to ‘float*’.
int foo(char *c) {...}
main() {
int (*thud)(void *);
thud = (int (*)(void *))(foo);
}
What actually happens during the evaluation of the assignment?
There is a difference between the cast type and foo; the cast type is a pointer and foo is a function. So, does the compiler convert what's in '(foo)' into a pointer to foo and only then make the cast? Because nothing else seems to make sense; the other option is that the function itself is converted to a pointer to a function that gets a void* and returns an int, and as far as I know a function is a label to a piece of code in memory and thus cannot become a pointer, which is a variable.
The name of a function is a pointer when used as such. It's somewhat similar to how the name of an array is a pointer to its first element.
That being said, calling a function through a pointer with a different type than the function's actual prototype (as your example does) is undefined behavior. Don't do it.
Addendum
If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
from section 6.3.2.3 of the C standard.
In C, absolutely nothing. It is just compiler glue to prevent you from doing something stupid. In C, the caller is responsible for maintaining the stack frame, so the cast is necessary when you invoke the function (i.e. arguments and the return value are pushed onto the stack). This makes it safe(r) since the caller's stack will not likely be mutated improperly. However, the invoked function can still mess up the caller's stack in certain rare cases.
I should clarify that the assignment copies the function pointer. But in C, all function pointers are just pointers. The type and casting is all compiler glue.
Another clarification: The standard specifies (in 6.5.2.2) that the behavior is undefined if the caller uses incompatible types. For example, casting a function which returns void into one which returns an int and then calling that function, the "returned" value is meaningless. It's a good idea to cast the function into a compatible type before calling it, or you may see unexpected results.
A pointer in C is an address, i.e. a number stored in some place. A function in C is an address to some code. Those two are one of the same.
The correct term is decay. Function foo decays to a pointer to foo before the cast. The cast itself will be a no-op on all platforms I can think of.
Note, however, that the behavior of a program containing such a cast is undefined by the C standard.
This was going to be a comment to Rick C. Petty's answer - but it doesn't fit in 300 characters.
The C standard is not very restrictive - pointers to objects (and functions aren't objects) can be converted to 'pointer to void' and back without problem. POSIX requires that pointers to functions are all the same size and can be converted to pointer to void.
POSIX 2008 - General Information - The Compilation Environment
2.12.3 Pointer Types
All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to void * shall not alter the representation. A void * value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.
Note:
The ISO C standard does not require this, but it is required for POSIX conformance.