When I try to use a static_cast to cast a double* to an int*, I get the following error:
invalid static_cast from type ‘double*’ to type ‘int*’
Here is the code:
#include <iostream>
int main()
{
double* p = new double(2);
int* r;
r=static_cast<int*>(p);
std::cout << *r << std::endl;
}
I understand that there would be problems converting between a double and an int, but why is there a problem converting between a double* and an int*?
You should use reinterpret_cast for casting pointers, i.e.
r = reinterpret_cast<int*>(p);
Of course this makes no sense,
unless you want take a int-level look at a double! You'll get some weird output and I don't think this is what you intended. If you want to cast the value pointed to by p to an int then,
*r = static_cast<int>(*p);
Also, r is not allocated so you can do one of the following:
int *r = new int(0);
*r = static_cast<int>(*p);
std::cout << *r << std::endl;
Or
int r = 0;
r = static_cast<int>(*p);
std::cout << r << std::endl;
Aside from being pointers, double* and int* have nothing in common. You could say the same thing for Foo* and Bar* pointer types to any dissimilar structures.
static_cast means that a pointer of the source type can be used as a pointer of the destination type, which requires a subtype relationship.
Floating point-to-integer conversion is supported, so int a = static_cast<int>(5.2) is fine. However, it's a conversion - the underlying data types are completely incompatible. What you're asking is for the runtime to convert a pointer to an 8-byte structure to a pointer to a 4-byte structure, which it can't do in any meaningful way.
That having been said, if you really want to interpret your double as an integer, int* r = reinterpret_cast<int*>(p) will work fine.
You can convert between a double and an int with static_cast<>, but not between pointers to different types. You can convert any pointer type to or from void * with static_cast<>.
The rationale may be that int * and double * are often effectively arrays, and the implementation doesn't know how big the array is.
Because you used double * instead of double
The * after it means that you are declaring a pointer, which is vastly different from a regular double.
C++ can not safely static_cast a pointer to a different type of pointer like that.
If you are wanting to do this kinda thing, you must first dereference the variable.
r=new int(static_cast<int>(*p));
You must use new because a double and an integer can not reside in the same memory space(sanely)
Related
Hello im confused by typecasting pointers.
I understand that void can hold any type.
So if i have an int * with a value inside of it then i make a void * to the int * can i then typecast it to a char?
It's quite hard to explain what i mean and the title might be wrong but will this work?
And is it safe to do this. I've seen it used quite alot in C. Is this bad practise.
int main() {
int* a = new int{ 65 };
void* v = static_cast<int*>(a);
std::cout << *(static_cast<char *>(v));
}
I understand that void can hold any type.
You understand it wrong. Pointer does not hold data, it points to it. So integer pointer points to memory with holds integer, float pointer points where float is etc. Void pointer just points to some memory, but it says - I do not know what kind of data is there.
Now conversion. Technically you can convert almost any pointer to any (there are exceptions like function pointers or pointers to a member, but they are completely different beasts and it is not what you are asking about) as they all just pointers. But problem happens when you try to access memory where they point to. So if you have int in memory and try to read it as float or vice versa - that's illegal. Now therte is one exception - you can access any data through pointer to char (or unsigned char) as char represents byte. Void pointer is irrelevant here, you can convert from pointer of one type to another without void * involved:
int main() {
int* a = new int{ 65 };
unsigned char *uptr = reinterpret_cast<unsigned char *>( a );
for( size_t i = 0; i < sizeof(int); ++i )
std::cout << static_cast<unsigned int>( uptr[i] ) << ' ';
}
Live example
Note I casted uptr[i] to unsigned int, that is to print bytes as numbers, as C++ stream ( std::cout in this case) will print character as symbol, which would be meaningless in this case.
First any object pointer can be cast to void*. That's the generic "could be anything" solution inherited from C and you should not use it. The C++ way is to use template<typename T> and T* for such cases where you need an anything pointer.
Secondly any object pointer can also be cast to char * as a special case. Casting to char* gives you access to the memory representation of the object. It does not convert the object to a char or anything, you get access to the raw bits for the value stored in the object.
So the reason why casting to void* and then to char* works is that casting any object pointer to char* is valid. The intermediate step to void* is not needed at all.
This only works for char* or unsigned char* or any other variant of char pointer, including std::byte*. You can not static_cast an int* to float* for example and using a void* and intermediate step will not make that work either.
Note: The value printed by the code is implementation defined since the memory representation of an int and a char is defined by the implementation. Specifically their endianness and size of an int and the signedness of char. Apart from that it perfectly valid code.
The question though is: Why do you want to do this?
Brushing up knowledge regarding (multidimensional) arrays / pointers conversions, the following two rules can explain some illegal conversions:
An T[M][N] decays to a T(*)[N], but not to a T** (as is explained in this SO entry)
There is not implicit conversion from T** to const T** (as is explained in the C++ faq)
So here is some test code written to try to cover different cases. The 3 cases we cannot explain are annotated with P1, P2 and P3.
int main() {
{
int arrayOfInt[3] = {0, 1, 2};
int * toPtr{nullptr};
int ** toPtrPtr{nullptr};
const int ** toPtrPtrConst{nullptr};
toPtr = arrayOfInt;
//toPtrPtr = &arrayOfInt; //KO, I assume because of 1.
//toPtrPtr = static_cast<int**>(&arrayOfInt); //KO, same as above
toPtrPtr = reinterpret_cast<int**>(&arrayOfInt);
toPtrPtr = (int**)&arrayOfInt;
//toPtrPtrConst = &arrayOfInt; //KO, still 1.
//toPtrPtrConst = static_cast<const int**>(&arrayOfInt); //KO, still 1.
toPtrPtrConst = reinterpret_cast<const int**>(&arrayOfInt); // (P1)
// it is supposed to be allowed to cast int** to const int* const*
// not const int**
// Why is it working without requiring a const_cast
// to cast away the const qualifier?
toPtrPtrConst = (const int**)&arrayOfInt;
//toPtrPtrConst = toPtrPtr; //KO, because of 2.
//toPtrPtrConst = reinterpret_cast<const int**>(toPtrPtr); //KO because of 2.
// so why is P1 allowed?
}
{
const int arrayOfConstInt[3] = {0, 1, 2};
const int * toPtrConst{nullptr};
const int ** toPtrPtrConst{nullptr};
int * const * toPtrConstPtr{nullptr};
toPtrConst = arrayOfConstInt;
//toPtrPtrConst = &arrayOfConstInt; //KO, I assume because of 1.
//toPtrPtrConst = static_cast<const int**>(&arrayOfConstInt); //KO, same as above
//toPtrPtrConst = reinterpret_cast<const int**>(&arrayOfConstInt); // (P2)
// Compiler error "casts away qualifiers",
// but which qualifier(s) would that cast away?
toPtrPtrConst = (const int**)&arrayOfConstInt;
//toPtrConstPtr = &arrayOfConstInt; //KO, I assume because of 1.
//toPtrConstPtr = static_cast<int * const *>(&arrayOfConstInt); //KO, same as above
toPtrConstPtr = reinterpret_cast<int * const *>(&arrayOfConstInt); // (P3)
// This one actually drops the const qualifier on the integer,
// but nevertheless it compiles
toPtrConstPtr = (int * const *)&arrayOfConstInt;
toPtrConstPtr = reinterpret_cast<int * const *>(&toPtrConst); // KO
// because it casts away const qualifier
// so why is P3 allowed?
}
}
Here it is in ideone: http://ideone.com/JzWmAJ
Why P1 allowed while it seems to violate 2.?
What are the qualifier(s) cast away by P2?
Why is P3 allowed when it actually casts away a const qualifier?
An explicit type conversion (a.k.a. "cast") doesn't violate a rule about implicit type conversions because rules about implicit type conversions are, to everyone's lack of surprise, not applicable to explicit type conversions.
const int (*)[3] is a type of a pointer-to-constant-something (where "something" is int[3]), whereas const int** is a type of a pointer-to-non-constant-something (where "something" is a pointer to a const int). The constant in pointer-to-constant-something gets stripped, which is not allowed.
No qualifier is stripped. toPtrConstPtr is a pointer-to-constant-something (where something is int *) and &arrayOfConstInt is a pointer-to-constant-something (where something is int[3]).
It should be noted that this is strictly language lawyer material. No normal programmer should allow any of these cast anywhere near their code.
All the casts in the code are bad, including all the ones that you didn't mark as bad.
If you do this:
cout << (uintptr_t)arrayOfInt << "\n";
cout << (uintptr_t)toPtrPtr << "\n";
you'll find that the output is identical. This is because &arrayOfInt has type int (*)[3]. When you dereference it with *&arrayOfInt, you get an array, which will decay back to a pointer of type int* with the same binary value as the pointer which you dereferenced. However, when you dereference an int**, you will load some bits from memory, and those bits will be the dereferenced pointer. These two dereferences are fundamentally incompatible. There is really no pretending that an int (*)[3] is the same as an int**.
T[N] are sometimes treated as a single type, as you use template-classes like std::array<T, N> or std::tuple<int, char, std::string>, and so it is treated like a single variable. Eg. jmp_buf of <csetjmp> is an array, but it is used like a normal variable. So while you are using fixed arrays in T[M][N], it is one linear block of N*M elements of type T as you write a member for each element in one struct, while pointer to pointer to T needs multiple memory-blocks, one memory-block for the pointer to the elements and the memory for each array pointed by the pointers. However arrays without fixed boundaries like T[] are treated like T*. When you use const T in an array, it works like a different type as T itsself, so you can c-cast an array of any type and if it is const or not, how you can cast char* to int*, c++ is strict and checks for const. The dereferenced arrayOfInt is an r-value, and reinterpret_cast sometimes has problems with r-values. Storing the dereferenced variable in a seperate variable and then reinterpret_cast it with the same type as a reference should work.
Lets assume there's variable of type int** which is the pointer to a 5x5 2D array:
int** ptr_array_5by5;
and a function with the following prototype:
void print2DArray_with5Columns(int (*ptr_row)[5]);
Is there a way to cast ptr_array_5by5 into some type which would match the type of the first argument of the function?
print2DArray_with5Columns( (<some casting type>) ptr_array_5by5)
The important thing to realize here is that int** is not
a pointer to a 2D array of anything. It is a pointer to a 1D
array of pointers. int (*)[5] is a pointer to a 2D array of
int (or more correctly, it is a pointer to the first element
of such an array). You can pretty much convert any pointer type
to any other pointer type using reinterpret_cast, but it is
also pretty much guaranteed not to work at runtime. (There are
special exceptions, but they are all very platform specific, and
very close to the hardware.)
If you really have an int** which points to 5 int*, each of
which points to 5 int, and you need an int (*)[5], the only
way you're going to be able to successfully convert is by doing
a conversion on the actual underlying data (which will involve
a copy), and not a conversion on the pointer. Something like:
int tmp[5][5];
for ( int i = 0; i != 5; ++ i ) {
int* row = ptr_array_5by5[i];
for ( int j = 0; j != 5; ++ j ) {
tmp[i][j] = row[j];
}
}
You can then pass tmp to your function without any casts: the
implicit conversion of array to pointer will convert
int [5][5] to int (*)[5].
Of course.
int** ptr_array_5by5;
void print2DArray_with5Columns(int (*ptr_row)[5]);
print2DArray_with5Columns( (int (*)[5]) ptr_array_5by5);
print2DArray_with5Columns( reinterpret_cast<int (*)[5]>(ptr_array_5by5));
The C language declaration syntax, for all its faults, lets you create casts by simply rewriting the declaration omitting any identifiers. It compiles, and it might even work.
There is a lot of confusion here because the descriptive wording does not match the C declarations. Here is some code that implements this (peculiar) cast and shows that it can work, just as I said.
void print2DArray_with5Columns(int (*ptr_row)[5]) {
for (int i = 0; i < 5; i++)
cout << (*ptr_row)[i] << " ";
cout << std::endl;
}
int main() {
int* a;
int** ptr_array_5by5;
a = new int[25];
for (int i = 0; i < 25; i++)
a[i] = i;
ptr_array_5by5 = (int**)a;
print2DArray_with5Columns((int (*)[5])(ptr_array_5by5));
return 0;
}
Please note that this declaration is not a 5x5 matrix. The cast is simply a pointer to an array of 5 ints, which decays to a simple array. This code generates a 5x5 flat matrix and prints the first row.
I suspect the real problem is that the cast is wrong and therefore the whole question is wrong.
The question has been asked whether this is the dreaded Undefined Behaviour. With suitable care it is not. The standard in effect allows any kind of a pointer-to-object to be cast to some other pointer-to-object or to a void pointer or to a large enough integer, and back again. [Pointer-to-function and pointer-to-member are treated a bit differently.] The round-tripped pointer is guaranteed to retain the same value. Therefore this cast is not UB provided the rules are followed, which is not that hard to do.
int** and int(*)[5] are different types (as n.m. pointed out)
You may treat an array as a pointer, e.g. int a[5]; *(a+1) = 6;
You may treat a pointer as an array, e.g. int *a = new int[5]; a[1] = 6;.
But treating object A as if it were an object B does not mean that it actually is object B.
What you can do though is declaring an int (*)[5], write the values of ptr_array_5by5 into it (after allocating memory, of course), and pass it to print2DArray_with5Columns.
On the other hand, yes there are casts that make your code compile. But I doubt that using one of them is getting you closer to your goal (see http://ideone.com/lVzNrN).
I think you are confused by assuming that pointers are arrays. Pointers are not arrays and vice-versa. If ptr_array_5by5 would be a 2D array then the parameter in prototype is fine for passing ptr_array_5by5 as argument. But you declared it as int **ptr_array_5by5, it is better to change the parameter to int ** type.
Why would you like to cast int **?
You use pointers to point to addresses.
So instead of type casting pointers, you have to point this pointer to some variable.
There is no meaning of type casting pointers because they themselves don't store values.
General syntax of pointer declaration: data-type *pointer_name;
A pointer is a variable whose value is the address of another variable, i.e., direct address of the memory location. Like any variable or constant, you must declare a pointer before you can use it to store any variable address. The data type of pointer must be same as the variable, which the pointer is pointing.
Why is it important that a pointer variable should contain the address of a variable of the same data type?
As a pointer has nothing to do with the value of another variable, why can't an integer pointer have the address of float data type variable?
Correct form:
int a = 10 ;
int *ptr = &a ;
ERROR, type mismatch
float a;
int *ptr; ptr = &a;
Because when you increase a pointer like
ptr++;
it will point to an address that is one multiplied with the size of the data type. If you do
ptr+=2;
and the data type is occupying 4 bytes which would be the case if the pointer was declared as
float *ptr;
the pointer will increase with 8 byte positions.
Before answering that, let me ask you, if it were valid, what would you do with such a pointer?
Suppose you have (inside a function)
float a;
int *ptr; ptr = &a;
*p = 1;
++*p;
return *p;
In such a case, there is no reason at all not to just use an int variable if it's an int you want.
Suppose you have (inside a function)
float a;
int *ptr; ptr = &a;
a = 3.14;
++*p;
return a;
In this sort of object aliasing, there can be a use, but allowing constructions like this is a pain for compilers. Compilers are free to assume, and some do assume, that the modification of *p has no effect on the value of a, so still return 3.14 unmodified;
If the latter case is something you're looking for, you can use union, which both makes your code clearer to other readers, and makes your code clearer to the compiler:
union {
float f;
int i;
} u;
u.f = 3.14;
++u.i;
return u.f;
So, to answer: it's to prevent you from shooting yourself in the foot. It's not allowed because there is no way such a pointer is useful. The one case where it could be useful is actually a case where it doesn't work, and there is another language construct that does handle it, and handles it correctly.
There is also the simple matter of dereferencing the pointer.
something = *pointer;
How much data should be read when the pointer is dereferenced? The compiler must know the data type in order to perform operations on the data like fetch, add, etc.
It's a safety feature. A pointer of one type pointing to a variable of another is a recipe for disaster; it has almost no legitimate purpose and almost anything you do with it is dangerous, especially if the types are of different sizes.
You can override this feature by means of casting. If you do so, you can no longer claim that you didn't know you were doing something dangerous.
It is possible for pointers to point to any data type: that's exactly what void pointers are for. You can use a void * to point to any data type1, and then you can get the original pointer back.
float f = 1.0f;
int i = 12;
void *p = &f;
p = &i;
Of course, you can't dereference a void pointer without first casting it back to the correct pointer type. It is up to you to ensure that the pointer type is correct.
// In C, this is valid: implicit conversions to void * and back.
float f = 1.0f;
void *p = &f;
float *fp = p;
printf("*fp = %f\n", *fp);
// In C++, you have to use a cast:
float *fp = static_cast<float *>(p);
Void pointers have limitations: you cannot dereference them, and you cannot do any pointer arithmetic.
1: Function pointers should not be cast to void *.
Lets understand it with an example.
int main()
{
char i = 8;
int *ptr = &i;
printf("Value of i = %d", *ptr);
return 0;
}
Ans: It will print some garbage because it dereferenced 4 byte in memory.
so if you do char *ptr = &i;, it will dereference 1 byte and so on..
It will give correct answer.
Because I followed a discussion where was told "Aliasing through incompatible pointer types is undefined behavior" (e.g. double d; int *p = (int *)&d; following question:
Is it allowed to cast an (double *) to (double **), e.g. double *d1; double **d2 = &d2 and using syntax like d2[0][y] expecting to be the same as d1[y]?
I know that it is not exactly aliasing through incompatible pointer types, but however I am not sure. Background is that I want to have a function which operates on 2-dimensional arrays (= images) but I want to be able to pass only a row or column of an image.
double** is incompatible with double*. The conversion is legal, but the only thing you can do with the results is cast it back. This seems more or less obvious: on a 32 bit machine, a double* won't even have the same size as a double.
But your example doesn't convert a double* to double**. It creates a new double**, which points to the double*. This is fine.
Is it allowed to cast an (double ) to (double *), e.g. double *d1; double **d2 = &d2 and using syntax like d2[0][y] expecting to be the same as d1[y]?
Of course not, and it won't even compile.
But if you meant:
double *d1;
double **d2 = &d2;
then yes, the above is perfectly valid and since d2[0][y] can be seen as (*d2)[y], there's really no problem at all.
Finally, remember that in the above code, you are not casting anything: &d2 is already of type double**. If you have to cast something, please use C++ style casts (static_cast, dynamic_cast, reinterpret_cast, etc..) and not C style casts.