Reputation: 147
As the title suggests, is it possible to start an array when you have the address given. i.e. If my start address is 10002432
,basically &array[0] = 10002432
and array[1]
will be stored in the next sequential address. I am able to copy to the said address using Pointers but instead of pointers, I would like to use arrays which are dynamically allocated. My current code is as follows which is using Pointers. I have tried various solutions that are currently are online but none of them are able to provide a definite solution.
I was thinking to make an overloaded new
operator, but i get stuck on it as well, since underlying the overloaded new operator we use malloc
which also doesnot allow to specify the start address.
I am running this code in a simulator (gem5), where I have control over memory addresses as I can specify how the virtual addresses map on the physical addresses.
int main(int argc, char const *argv[])
{
uintptr_t address = 10002432;
int *Pointer = (int*) address;
int *Pointer2 = Pointer;
for (int i = 0; i < 32; ++i)
{
*Pointer = i;
Pointer = Pointer + 4;
}
for (int i = 0; i < 32; ++i)
{
printf("%d\n",Pointer2 );
Pointer2 = Pointer2 + 4;
}
}
Any assistance would be highly appreciated.
Upvotes: 1
Views: 859
Reputation: 238351
No, it is not possible to "allocate" memory from specific address in standard C++.
Within an operating system that uses virtual memory (i.e. all modern multi-tasking operating systems), there is typically no system specific way to do such allocation either. It is however common to use constant addresses documented by the system provider on embedded systems. You can typically consider such memory "always allocated". Perhaps this applies to your simulator.
You can create dynamic objects into uninitialised memory through a pointer. The syntax for doing this is called placement new. Ths is sometimes useful to reuse "generic" allocated memory for some in-place containers such as std::vector
, as well as in the mentioned case where the address is constant.
Important note: The address is assumed to satisfy the alignment requirement of the created object. If it is not satisfied, then the behaviour of the program is undefined.
Although there is syntax for doing so, it is not possible to create an array this way in practice. Array placement new is not guaranteed to start the array from the provided address. Some implementation specific amount of memory at the beginning from the address may be used by the language implementation.
Instead of creating an array, you can simply create the elements individually, separately, and pretend that they are in an array. This is what the implementation of std::vector
in your standard library probably does as well. There are helper functions for that in the standard library:
using T = int;
std::uintptr_t address = 10002432; // Note: using hex would be more conventional
const std::size_t size = 32;
T* p = std::launder(reinterpret_cast<T*>(address));
if (!std::align(alignof(T), sizeof(T), p, sizeof(T)))
throw std::invalid_argument("bad alignment");
std::uninitialized_fill_n(p, size, T{});
// use it
std::destroy_n(p, size);
// Instead of filling from the value initialised value,
// you can copy a range. Here is an example with C++20 ranges:
auto i = std::views::iota(T{});
std::uninitialized_copy_n(i.begin(), size, p);
// use it
std::destroy_n(p, size);
Note that the destruction part is unnecessary in case of int
and other trivial types, but mandatory for non-trivially-destructible types. This is a trivial example, and is not exception safe. If exception is thrown after successful initialisation, the destruction won't be called. You can use a RAII idiom to make sure the destruction happens.
If the given pointer is derived from a valid pointer to previously existing objects such as when reusing memory of a trivial object (which is not the case in the example), then std::launder
is necessary as used in the example. It may be unnecessary otherwise (which is the case in the example, so you may get rid of laundering if you know what you're doing; but keeping it doesn't hurt).
When reinterpreting integer as pointer, the language standard provides no guarantee what memory address the value will be mapped to. You have to rely on guarantees provided by the language implementation for that memory address to be in any way useful. Except in the case you converted from a valid pointer into an integer (of sufficient size), and back to same pointer type, then that is guaranteed to be the same pointer value. But this case does not apply in the example.
Upvotes: 1