Is the following well defined behavior?
#include <cstdlib>
#include <iostream>
void reallocate_something(void *&source_and_result, size_t size) {
void *dest = malloc(size);
memcpy(dest, source_and_result, size);
free(source_and_result);
source_and_result = dest;
}
void reallocate_something(int *&source_and_result, size_t size) {
// I the cast safe in this use case?
reallocate_something(reinterpret_cast<void*&>(source_and_result), size);
}
int main() {
const size_t size = 4 * sizeof(int);
int *start = static_cast<int*>(malloc(size));
std::cout << start << ' ' << *start << '\n';
reallocate_something(start, size);
std::cout << start << ' ' << *start << '\n';
return 0;
}
The code uses a reinterpret_cast
to pass a reference to a pointer and re-allocate it, free it, and set it to the new area allocated. Is this well defined?
In particular A static_cast
would work if I did not want to pass a reference, and that would be well defined.
The tag is C , and I'm asking about this code as-is within the C standard.
CodePudding user response:
Is the following well defined behavior?
No, it's not. You can't interpret int *
pointer with void *
handle, int
and void
are not similar types. You can convert an int *
pointer to void *
and back. If your function takes a reference, to do the conversion you need a new temporary variable of type void *
to hold the result of the conversion, and then you have to assign it back, like in the other answer https://stackoverflow.com/a/69641609/9072753 .
Anyway, just make it a template, and write nice C code with placement new. Something along:
template<typename T>
void reallocate_something(T *&pnt, size_t cnt) {
T *dest = reinterpret_cast<T *>(malloc(cnt * sizeof(T)));
if (dest == NULL) throw ...;
for (size_t i = 0; i < cnt; i) {
new (dest[i]) T(pnt[i]);
}
free(static_cast<void*>(pnt));
pnt = dest;
}
CodePudding user response:
Actually I'm not sure but I feel this is the correct way to do.
#include <iostream>
#include <cstring>
void reallocate_something(void *&source_and_result, size_t size) {
void *dest = malloc(size);
memcpy(dest, source_and_result, size);
free(source_and_result);
source_and_result = dest;
}
void reallocate_something(int *&source_and_result, size_t size) {
// Is the cast safe in this use case?
void *temp = static_cast<void*>(source_and_result);
reallocate_something(temp, size);
source_and_result = static_cast<int*>(temp);
}
int main() {
const size_t size = 4 * sizeof(int);
int *start = static_cast<int*>(malloc(size));
std::cout << start << ' ' << *start << '\n';
reallocate_something(start, size);
std::cout << start << ' ' << *start << '\n';
return 0;
}
CodePudding user response:
This is not well defined for void*
and int*
are not similar. Refer to Type aliasing section here.
Note that pointer round trip via void*
like below is well defined. Particularly, there is no type aliasing here.
T* pt = ...;
void* p = pt;
auto pt2 = static_cast<T*>(p);
assert(pt2 == pt);
This is different from following code with type aliasing which is not well defined.
T* pt = ...;
void* p = nullptr;
reinterpret_cast<T*&>(p) = pt; // or *reinterpret_cast<T**>(&p) = pt;
auto pt2 = static_cast<T*>(p);
assert(pt2 == pt);
It follows that your sample code can be revised as below.
void reallocate_something(int *&source_and_result, size_t size) {
void* p = source_and_result;
reallocate_something(p, size);
source_and_result = static_cast<int*>(p);
}
Or better yet
void* reallocate_something(void *source_and_result, size_t size) {
void *dest = malloc(size);
memcpy(dest, source_and_result, size);
free(source_and_result);
return dest;
}
void reallocate_something(int *&source_and_result, size_t size) {
source_and_result = static_cast<int*>(reallocate_something(source_and_result, size));
}