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);
Related
Do both statements mean the same thing that p is pointing at address location 10?
On compilation, the first initialization gives some warning. What's the meaning of that?
#include <stdio.h>
int main()
{
int *p = 10;
int *q = (int *)10;
return 0;
}
output:
warning: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [- Wint-conversion]
Both cases convert the integer 10 to a pointer type which is used to initialize an int *. The cast in the second case makes it explicit that this behavior is intentional.
While converting from an integer to pointer is allowed, the assignment operator (and by extension, initialization) does not specifically allow this conversion, so a cast it required to be conforming. Many compilers however will still allow this and simply issue a warning (as your apparently does).
Note however that actually attempting to use a pointer that is assigned a specific numeric value will most likely cause a crash unless you're on a embedded system that supports reading or writing specific memory addresses.
int *p = 10; is incorrect (constraint violation), and the compiler must produce a diagnostic message. The compiler could reject the program, and there is no behaviour defined if it doesn't. The rule is that the initializer for a pointer must be a compatible pointer value or a null pointer constant.
int *q = (int *)10; means to convert the integer 10 to a pointer. The result is implementation-defined and it could be a trap representation, meaning that the initialization causes undefined behaviour if execution reaches this line.
int and pointer to an integer int* are different types. The 10 on the first line is an int that you are trying to assign to a pointer to int type. Hence the warning. (on X86 both share the same size, but consider that mostly coincidence at this point).
By casting the int to a pointer, like you do on the second line, you are telling the compiler "Hey, I know these are different types but I know what I'm doing, so go ahead and just treat the value 10 like a pointer because I really do want to point at the memory with an address of 10". (in almost every case the memory address of 10 is not going to be usable by you)
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);
~~ ^~
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.
What is really happening here when we provide a definition like below?
int * a = 2;
What is really happening behind the scenes?
SOLVED
Here a will point to memory address 2.
The result of conversion from integer to a pointer is implementation-defined in C, quoting C99 Section 6.3.2.3:
5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
So you shouldn't rely on such conversion except when the literal is 0 which will give you a null pointer.
In practice you are likely to find that the pointer will hold the memory address specified by the integer constant:
#include <stdio.h>
int main(void) {
int * a = 2;
printf("%p", a); /* prints 0x2 in gcc */
return 0;
}
Most compilers will also warn about this unsafe cast :
$ gcc test.c test.c: In function ‘main’: test.c:4:13: warning:
initialization makes pointer from integer without a cast [enabled by
default] int * a = 2;
^
In here the memory for "a" will not be allocated. So, it gives the segmentation fault error.
It will try to take the 2 as a address and pointer a will try to point the address 2. But it may be our system does not have that address or it may be allocated for some other process.
So, in the compiling time it will give the one warning message.
Warning: initialization makes pointer from integer without a cast [enabled by default]
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.