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);
~~ ^~
Related
I am currently doing pointers. I have been programming for a long time, but not in C/C++. With that being said, my pointer knowledge is abysmal.
Currently, I am following a guide on YouTube and he prints the code below.
int main() {
int a = 5;
int *p;
p = &a;
printf("%d\n", p);
}
This prints successfully for him, and he sees a memory location. For me, I see the error
warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *'
From this, I expect I need to put an & in front of the p to make it print the value. But then I receive this error,
int main() {
int a = 5;
int *p;
p = &a;
printf("%d\n", &p);
}
'int', but argument 2 has type 'int **'
Where is the hole in my knowledge? Any key tips or strategies when working with this, I don't know why I find this so abstract.
Thanks,
I was expecting the value to print as expected, but instead am greeted with the error.
%d is the wrong format specifier for pointers. That may work on a more lenient or noncompliant implementation, but you should use %p to print pointer values.
Warnings are not errors. You're receiving a warning, which is not stopping your program from working, because the print specifier %d (ie printf("%d")) is for displaying integers, and you're giving it a non-integer argument of type int*.
The problem here is not with the argument, it's with the print specifier. Your attempt at a fix just changes the int* to an int**, which still does not match the format specifier %d. Use %p instead, which is the specifier for pointers, and will fix the warning, and print the address in hexadecimal notation.
You could also suppress the warning with a series of explicit casts from int* to int, but integer representations of memory addresses are generally much less used than hexadecimal representations in the first place.
Note that using wrong format specifier in printf() lead to undefined behaviour1).
The correct format specifier to print a pointer is %p format specifier.
Remember, format specifier %p expect that the argument shall be a pointer to void, so you should type cast pointer argument to void *. The correct statement to print pointer p would be:
printf("%p\n", (void *)p);
C11#7.21.6.1p9 [emphasis added]
9 If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
suppose we have the following declarations in C :
double d[25], * p;
int * t;
char * c;
How can we explaine the following printf() results ?
printf("d : %x\t",d);
printf("p : %x\t",p);
printf("t : %x\t",t);
printf("c : %x\t",c);
which print the ligne :
d : 62fd30 p : 1 t : 0 c : 39
We can clairly see the memory adress of d but both p, t and c don't look like adresses. I used to think that a uninitialized pointer takes the Null value after its declaration. Am I wrong? how can we explain these results?
All automatic (non-static local) variables will by default be uninitialized with an indeterminate value (that can seem like random or garbage). It doesn't matter if the variable is a pointer or not.
Also, just reading the value of an uninitialized pointer isn't automatically UB (in C), but dereferencing an uninitialized pointer definitely is.
However, as mentioned in one comment, you need to use "%p" to print pointers (technically they need to be casted to void * as well). Mismatching printf format specifier and argument type do lead to UB.
You cannot assume any pointer will be zero-initialized after declaration - C standard does not imply anything at that point. Same goes with other types of values. This is why it is a good practice to set a value of a variable upon declaration, like:
int * t = NULL;
Some compilers do zero-initialize variables, but this is tool-specific feature.
I used to think that a uninitialized pointer takes the Null
value after its declaration. Am I wrong?
Yes, your assumption are incorrect. An uninitialized pointer declared with automatic storage always contains garbage or junk data i.e not a valid address, hence its better to initialize with NULL at first while declaring. For e.g
double *d = NULL;
/* some processing */
if(d == NULL) {
/* #TODO error handling. Not allowed to de-reference NULL pointer */
}
Here
double d[25];
printf("d : %x\t",d);
d is an array of 25 double's & array name itself address, while printing d using %x causes undefined behavior, even your compiler could have warned you like this
main.c:5:19: warning: format specifies type 'unsigned int'but the
argument has type 'double *' [-Wformat]
But you seems to ignore compiler warnings, one shouldn't. Always compiler your code with minimal flags like -Wall. For e.g
gcc -Wall -Werror -Wpedantic test.c
To print array name, use %p format specifier. for e.g
double d[25];
printf("Array d : %p\t",(void*)d);
Similarly with int pointer t and char pointer c, use %p format specifier instead of %x. Also don't keep any uninitialized pointer in your code.
int * t; /* initialized with valid address else
dereferencing uninitialized pointer causes UB */
printf("t : %p\n",(void*)t);
int main()
{
int a;
int* b;
a = 40;
b = &a;
printf("the address of a is %p, and the value of a is %d \n",&a, a);
return 0;
}
I find that both (void*)&a and &a print the same thing. So why do people still add (void*)? Is it just a habit?
You use specifier %p to print address stored in pointer, and this format specifier expects type to be void * . And as &a is of type int * , cast void * is used .
The printf() format specifier %p expects a void* type pointer. Since what you are passing might not be a void* type pointer, and the standard does not mandate for all pointers to have the same format, is is important that you cast a pointer to void* before passing it to printf.
For instance:
int* a = malloc(sizeof(int));
printf("a is %p",(void*)a);
Is done as best practice incase int* and void* are not similar
C Standard says that using an incorrect specifier for an argument in printf will result in undefined behavior.
7.21.6.1 The fprintf function
If a conversion specification is invalid, the behavior is undefined. 282) If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
Since pointer to int is not the same type as pointer to void, and %p may only be used for a pointer to void and even if some other rules says that any pointer may be converted to pointer to void and back, that doesn't change the fact that the behavior is undefined, because of the quoted rule.
Firstly, different pointer types are not necessarily interchangable.
Secondly, for varargs no implicit conversion takes place as the compiler does not know the expected type.
I wrote this simple program
#include <stdio.h>
int main(void)
{
int array[10];
printf("sizeof(array): %lu\n", sizeof(array));
printf("sizeof(&array): %lu\n", sizeof(&array));
printf("array: %p\n", array);
printf("&array: %p\n", &array);
printf("value: ");
scanf("%d", array); // 1
//scanf("%d, &array"); // 2
}
Output:
sizeof(array): 40
sizeof(&array): 8
array: 0x7fffaab6f480
&array: 0x7fffaab6f480
value: 10
It compiles when I use 1. However, it doesn't when I use 2!
I get this compilation warning
warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type
‘int (*)[10]’ [-Wformat=]
scanf("%d", &array);
^
although array and &array have the same value, they have different sizes (40 and 8 respectively) on my system.
On the other hand, this code
int array[10];
fread(&array, x, y, z);
compiles and works perfectly as
int array[10];
fread(array, x, y, z);
I noticed in the warning message that &array has the type int (*)[10]. Now what does that mean?
I also believe I have no troubles with fread() because it accepts a void *, but what about scanf()? How does it differentiate between pointer types?
And why doesn't it treat array as &array even though they're practically have the same value?
Arrays decays to pointers, so using the array as argument is the same as passing a pointer to the first element.
When you're doing &array, you get a pointer to the array (the int (*)[10] thing), and not a pointer to an integer.
With the scanf and printf family, you must pass the exact type that they expect (after the default argument promotions are applied - but that is not relevant in this example).
This is because the arguments correspond to a ... in the prototype, meaning that the compiler does not attempt to convert your argument to the expected type. If you use the wrong type you just get undefined behaviour.
This happens in the second case, scanf("%d", &array). As your examples show &array does not have type int *, therefore the behaviour is undefined.
In practice, it is likely to work anyway if your compiler uses the same representation for int * as it does for int (*)[10], which all modern compilers do AFAIK. But , of course, you should not rely on undefined behaviour, especially when there is an easy fix available.
In the fread example, it matches the prototype parameter void *. Therefore the compiler converts your argument to void *. Since the first element of an array is at the same memory address as the array itself, (void *)&array == (void *)array, even though array and &array have different types (and possibly even different representations), they both point to the same memory address.
First, your program was compiled very well - warning is not an error.
The warning message tells you that there's mismatch between format and type: "%d" format requires int *, but you sent &array, whose type is int (*)[10].
array is 10-length array of int. Its type is int [10]. However, array can be converted to pointer, that is, &array[0].
When you try to pass array to function, you actually pass a pointer, &array[0]. So both this code
printf("%d", array);
scanf("%d", array);
and this another code
printf("%d", &array[0]);
scanf("%d", &array[0]);
are the same.
Now let's look at another thing, &array. Its type is int (*)[10], that is, pointer to array.
Of course, the actual values of both &array[0] and &array are the same, but they have different types. So they're diferent things, as 'A' and 65 are different.
Altough it looks okay since the values are the same, I don't think it is such a good practice - Use array or &array[0].
Both are syntactically correct C, and do exactly the same thing, as you suggest. However, one is more correct than the other. That said, neither is perfect.
To be properly pedantic, you should have this:
scanf("%d", &array[0]);
.. because that's a pointer to an integer, array[0], and what you really want.
In fact, you can pass anything to scanf (if you ignore the documentation), and the compiler is expected to accept it (misuse of the standard library does not trigger an error, traditionally). If you pass something invalid then your program is expected to fail at runtime (or not, if you're lucky -- undefined behaviour is like that).
However, your compiler is trying to be helpful and interpret the scanf format string for you to make sure you did the right thing, but if you look carefully, it's not an error, its a warning. You're free to ignore it, if you choose.
The reason you don't get an error with fread is because that function does not take an integer, it takes void *, and both forms can be converted to that type just as easily.
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.