I am trying to understand the pointer concepts in-depth.
In the following code,
#include <stdio.h>
int main()
{
int i = 10;
int *iptr = &i;
printf("(float)* : %f\n", (float)*iptr);
printf("(float*) : %f\n", (float*)iptr);
printf("*(float*) : %f\n", *(float*)iptr);
return 0;
}
output:
(float)* : 10.000000
(float*) : 10.000000
*(float*) : 0.000000
I also got a warning for the type-cast (float*).
I find it difficult to even analyse the difference. If anyone can help me to analyse what is the exact usage of all three, it would be helpful.
The difference is
You are dereferencing the int and casting it to float in
printf("(float)* : %f\n", (float)*iptr);
which is fine.
You are casting the int pointer to a float pointer, and printing the float pointer with the "%f" specifier is undefined behavior, the correct specifier for printing pointers is "%p", so
printf("(float*) : %f\n", (float*)iptr);
is wrong, it should be
printf("(float*) : %p\n", (void *) iptr);
casting to float * here is not meaningful, because the void * address is the same as the float * address and also the int * address, the difference would be when you do pointer arithmetic.
You are casting the int pointer to a float and dereferencing the resulting float pointer, although it will violate strict aliasing rules in
printf("(float*) : %f\n", *(float*)iptr);
which is also undefined behavior
The first one is correct.
i is an int variable, and iptr a pointer to that int.
(float)*iptr: *iptr dereferences iptr, which returns an int. Then that int is converted to a temporary float containing the same value. And that float is used by printf.
*(float*)iptr: Attempts to cast a pointer-to-int into a pointer-to-float. This is invalid, and should produce a compiler warning or error. It creates a pointer with the same address, but with the type saying that it points to a float value.
The * operator then dereferences it, so the int is read as if it were a float. So the resulting float would be invalid, and it could result in a segfault because floats are longer than ints, so it reads more memory than there is allocated for the int.
(float*)iptr: Same problem, but it doesn't dereference the (invalid) pointer, and passes a pointer-to-float into printf, instead of a float. But printf expects a float. Some compilers should also produce a warning/error here because the format string indicates what value types are expected.
If the format specifier indicates %p, it expects a pointer (void*, float*, or any other). It will then print out the address, and not the value it points to. This can be useful in debugging for example.
Related
Why doesn't typecasting work here..?
#include<stdio.h>
int main(){
int x = 5;
float y = 7.0;
float *p = &y;
int *q = &x;
printf("p is %d\nq is %d\np - q is %d", p, q, (p - q));
return 0;
}
I am getting this error invalid operands of types 'float*' and 'int*' to binary 'operator-'; what does it mean?
The error means that the compiler is unable to deduce the common type of two operands one of which has the type float * and other int *. There is no implicit conversion between these types.
But in any case the program has undefined behavior because at least you may not subtract two pointers that do not point to elements of the same array or to a memory after the last element of the same array.
From the C Standard (6.5.6 Additive operators)
9 When two pointers are subtracted, both shall point to elements of
the same array object, or one past the last element of the array
object; the result is the difference of the subscripts of the two
array elements.
And using incorrect conversion specifiers (as for example %d with a pointer) for supplied arguments in the function printf also invokes undefined behavior.
From the C Standard (7.21.6.1 The fprintf function)
9 If a conversion specification is invalid, the behavior is
undefined.275) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.
I suppose you want to subtract the actual values your pointers are pointing to.
C converts int to float implicitly when you are operating on float and int.
Also, you program has some problems as
Not using %f type specifier to print float pointer
Not using the * operator to access pointer values.
Here is your program, working:
#include <stdio.h>
int main()
{
int x = 5;
float y = 7.0;
float *p = &y;
int *q = &x;
printf("p is %f\nq is %d\np - q is %f", *p, *q, (*p - *q));
return 0;
}
With the outputs:
p is 7.000000
q is 5
p - q is 2.000000
You can't subtract pointers like that and get a meaningful result in standard C.
Per 6.5.6 Additive operators, paragraph 9 of the C11 standard:
When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements.
In this case, a single int or float variable is considered to be an array of size 1.
So given
int x = 5;
float y = 7.0;
float *p = &y;
int *q = &x;
trying to compute the value p - q results in undefined behavior.
Per J.2 Undefined behavior:
The behavior is undefined in the following circumstances:
...
Pointers that do not point into, or just beyond, the same array object are subtracted
However, code like this likely won't cause problems as it's merely subtracting two integer values (although I haven't checked thoroughly), but the result doesn't have to be meaningful:
int x = 5;
float y = 7.0;
float *p = &y;
int *q = &x;
intptr_t diff = ( intptr_t ) p - ( intptr_t ) q;
Edit: As has been pointed out in the comments, subtracting two void* pointers is not proper standard C. If you want to subtract two pointers to find the distance between them the correct approach is to cast them to an appropriately sized integer, and then do integer arithmetic.
E.g.:
printf("p is %p\nq is %p\np - q is %ld\n", p, q, ((intptr_t)p - (intptr_t)q));
Original answer:
It means that the minus operator is not defined for mixed types of pointers. If you want to subtract those two pointers, for example to find the amount of space between them, the better option would be to cast them both to void* pointers.
Also, you should print pointer values with the %p specifier instead of %d.
E.g.:
printf("p is %p\nq is %p\np - q is %ld\n", p, q, ((void*)p - (void*)q));
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'm trying to do the following
int a[8]={1,2,3,4,5,6,7,8};
printf("%f\n", *(float *)a);
printf("%f\n", *((float *)a+1));
printf("%f\n", *((float *)a+2));
printf("%f\n", *((float *)a+3));
printf("%f\n", *((float *)a+4));
printf("%f\n", *((float *)a+5));
printf("%f\n", *((float *)a+6));
printf("%f\n", *((float *)a+7));
I get
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
The reason why I'm trying to print the elements in this way is because, I want to cast the int pointer to the array to the float pointer and pass it as a parameter for another function which only takes float *.
It seems that this does not work well. Can someone explain why this is not working?
int *ptr;
function((float *)ptr);
If I do this the function does not read the values the pointer is pointing to properly.. just returning 0.0000.
This is not correct. int and float are not guaranteed to have the same alignment.
Remember: Casting a value and casting a pointer are different scenarios. Casting a pointer changes the way to refer to the type value, which can almost certainly result in a mis-alignment in most of the cases.
As per C11 standard document, chapter ยง6.3.2.3
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined.
In your case, a work-around may be
printf("%f\n", (float)*a); //cast the value, not the pointer
You cannot cast a pointer to int to a pointer to float, and expect to get your value converted to the corresponding number in floating point representation. Casting a single value works, but casting by changing a pointer type does not alter the representation.
If you need an array of floats, declare an array of floats, and cast one element at a time:
float b[8];
for (int i = 0 ; i != 8 ; i++) {
b[i] = a[i];
}
func_expects_float(b, 8);
Casting an int pointer to a float doesn't convert the integer to a floating point number. Casting just tells the machine to use the contents of the memory location pointed to by the pointer as floating point value instead of an integer value. But it doesn't change the value from integer representation to floating point representation.
you might try:
printf( "%f\n", 1.0f * a[0]);
printf( "%f\n", 1.0f * a[1]);
....
==or==
printf( "%f\n", *(a+0) * 1.0f );
printf( "%f\n", *(a+1) * 1.0f );
....
We have some float x and we convert it into i, but could anyone please explain what does do in depth first &, then cast and finally *?
int i = *(int*)&x;
&x
Gets a pointer to x
(int*)&x
Casts that pointer to an int*, i.e. a pointer to an int
*(int*)&x
Dereferences the resulting pointer, reading the memory of variable x as if it were an int.
Without knowing the type x is, it's hard to tell what the code's purpose. Most likely, if x is a float, it's being used to get the binary representation of the float (which is impossible to do by just casting to an int, because it does a float to int conversion)
&x: Get a pointer to x
(int*): Cast that pointer to a pointer to an int - this does not actually cast x
*: Retrieve the value the pointer points to
Since a pointer cast doesn't actually perform type conversion, this line of code has the effect of reading the internal bits used to store the float x, and storing those bits int the integer i. Basically, this is a reinterpret_cast. (reinterpret_cast is a C++ feature, but it does exactly the same thing as this C syntax.)
"&" means getting the reference of a variable. So &x gets the reference to a float and has type float*.
(int*) is a cast. Therefore you are casting from float* to int*.
The last "*" is for dereferencing the int*, that is, getting the value of the int*, which is an int.
Lets break it down
&x; //this gets you the address of x
(int *)&x; //this cast the address to be pointing at an integer value
//(before it was pointing at a float value)
*(int *)&x; // now the last * dereferences that value
assuming in your system, long is 8bytes and int is 4bytes. so you get only 4 bytes of the 8 bytes of x. As int is usually 4 bytes and long 8 bytes. Now which of the 4 bytes you get (the first 4 or the last 4) depends on the endianness of your system..
For more info on endianness, read this http://en.wikipedia.org/wiki/Endianness
I tried to run this code,
int *p;
float q;
q = 6.6;
p = &q;
Though it will be a warning, but i think &q and p are of same size, so p can have an address of q. But when I print &q and p I am getting different output.
This is my output
*p = 6.600000
q = 0.000000, p = 0x40d33333, &q = 0x7fffe2fa3c8c
What is that I am missing?
And p and &q is same when both pointer and variable type is same.
My complete code is
#include<stdio.h>
void main()
{
int *p;
float q;
q = 6.6;
p = &q;
printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);
}
You need to take compiler warnings more seriously.
C doesn't require compilers to reject invalid programs, it merely requires "diagnostics" for rule violations. A diagnostic can be either a fatal error message or a warning.
Unfortunately, it's common for compilers to issue warnings for assignments of incompatible pointer types.
void main()
This is wrong; it should be int main(void). Your compiler may let you get away with it, and it may not cause any visible problems, but there's no point in not writing it correctly. (It's not quite that simple, but that's close enough.)
int *p;
float q;
q = 6.6;
That's ok.
p = &q;
p is of type int*; &q is of type float*. Assigning one to the other (without a cast) is a constraint violation. The simplest way to look at it is that it's simply illegal.
If you really want to do this assignment, you can use a cast:
p = (int*)&q; /* legal, but ugly */
but there's rarely a good reason to do so. p is a pointer to int; it should point to an int object unless you have a very good reason to make it point to something else. In some circumstances, the conversion itself can have undefined behavior.
printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);
The %f format requires a double argument (a float argument is promoted to double in this context so float would be ok). But *p is of type int. Calling printf with an argument of the wrong type causes your program's behavior to be undefined.
%p requires an argument of type void*, not just of any pointer type. If you want to print a pointer value, you should cast it to void*:
printf("&q = %p\n", (void*)&q);
It's likely to work without the cast, but again, the behavior is undefined.
If you get any warnings when you compile a program, don't even bother running it. Fix the warnings first.
As for the question in your title, pointers of type int* and float* are of different types. An int* should point to an int object; a float* should point to a float object. Your compiler may let you mix them, but the result of doing so is either implementation-defined or undefined. The C language, and particularly many C compilers, will let you get away with a lot of things that don't make much sense.
The reason that they're distinct types is to (try to) prevent, or at least detect, errors in their use. If you declare an object of type int*, you're saying that you intend for it to point to an int object (if it's not a null pointer). Storing the address of a float object in your int* object is almost certainly a mistake. Enforcing type safety allows such mistakes to be detected as early as possible (when your compiler prints a warning rather than when your program crashes during a demo for an important client).
It's likely (but not guaranteed) that int* and float* are the same size and have the same internal representation. But the meaning of an int* object is not "a collection of 32 (or 64) bits containing a virtual address", but "something that points to an int object".
You're getting undefined behaviour, because you're passing the wrong types to printf. When you tell it to expect a float, it actually expects a double - but you pass an int.
As a result it prints the wrong information, because printf relies entirely on the format string to access the arguments you pass it.
In addition to what is said by teppic,
Consider,
int a = 5;
int *p = &a;
In this case we indicate to the compiler that p is going to point to an integer. So it is known that when we do something like *p , at runtime, the no. of bytes equal to size of an int would be read.
If you assign address of a datatype occupying x number of bytes to a pointer of which is declared to hold the address of datatypes of fewer bytes than x, you read the wrong number of bytes when using the indirection operator.