Assign to a specific memory address

Suppose some address like 0x25D4C3FA is available on my memory. I want to assign to that address. One way that firstly comes to mind is like:

1
2
int* p = (int*) 0x25D4C3FA;
*p = 6;

But I get the exception error below for the second line:
Exception thrown: write access violation.
p was 0x25D4C3FA.


Is really there any simple way to be able to assign to a specific memory address in C++?
Last edited on
I think what you have written should work assuming you are absolutely sure that is a valid memory address that your process is allowed to write to. Question is, how do you know? Objects might end up with different addresses each time you run your program.

Note that you cannot normally use this to access the memory of other programs. Modern computers use virtual memory so each process has its own address space. The same virtual memory addresses might be used by different processes but in that case they would normally refer to different positions in physical memory.

If you want to share memory between processes you might want to use something like mmap on Linux. Then you could have two processes accessing the same memory, but the virtual addresses that are used by the two processes to access this memory might still be different.
Last edited on
This is the program on Visual Studio 2022 which is quite simple:
1
2
3
4
5
6
7
8
int main() {

	int* p = (int*) 0x25D4C3FA;
        *p = 6;
	
	std::system("pause");
	return 0;
}
When I set a breakpoint and debug my program and then go Debug -> Windows -> Memory -> Memory 1, that address is available.
https://imgur.com/a/5BY253G
How to know my process space address range?
Last edited on
Why do you want to do this? You are circumventing the C++ memory management system, and now you are managing the memory in your head.

If you change line 3 to int* p = new int;, you get essentially the same thing, but without having to manage the memory yourself. But you know that already. So, what advantage are you trying to gain by doing this the hard way? Or are you just experimenting for the sake of experimenting?

Not all addresses are mapped to anything. That is probably what ?? means. Trying to access those unmapped memory addresses will crash your program.
Last edited on
Or are you just experimenting for the sake of experimenting?
Yes. Just trying.

That is probably what ?? means. Trying to access those unmapped memory addresses will crash your program.
Probably the reason for the exception error I got earlier.

I tried to take an available address within my process space range by repeating lines 2 and 3 of the code below 6 times and then grabbed one of them:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
	int a{ 4 };
	std::cout << &a << '\n';
	// 1038B5F724
	// 188B0FF6A4
	// 1317CFFC14
	// DA453BF544
	// 68A0F9FCC4
        // 1A3F55FAF4

	int* p = (int*)0x1317CFFC14;
	std::cout << p << '\n'; // prints 0x1317CFFC14;
	*p = 6;
}
Yet the exception error rises. I think the reason is, that memory address is not necessarily allocated for use.

Hence, we can't assign to a specific address in memory and it's done by the system or as doug4 said, C++ memory management system. Of course, in Embedded programming or related low level coding, things like that makes more sense.
Last edited on
Modern operating system use "protected mode", which means that each process has its own separate (virtual) memory space. This memory space is 4 TB in size for 32-Bit processes. For 64-Bit processes, it is even 16 Exabytes in size 😲

But: Just because those (virtual) memory addresses "exist" in your process' memory space, it does not mean that they all are actually mapped to anything! For example, if you sum up the total sizes of the individual (virtual) memory spaces of all the processes that are running on your machine, then this will be way bigger than what actually fits into your (physical) RAM. However, that is perfectly okay, because most of those virtual addresses simply are not mapped to physical addresses in the RAM, or to anything else.

Simply put, some memory addresses are allocated "statically" when your program is starting up (e.g. global variables). Additional memory addresses may be allocated "dynamically" at runtime, e.g. by using malloc() or similar functions. And then there is the "stack", which is used to hold the local variables of the current function. But, any memory address that has not been "allocated" before, in one way or another, must be considered "invalid" (inaccessible). Any attempt of trying to access such an "invalid" memory address results in an access violation (segmentation fault) hardware exception – which usually causes your program to terminate abnormally ("crash").

Therefore, trying to access some arbitrarily chosen address, e.g. 0x1317CFFC14, is likely to crash your program with an "access violation" error, because there is nothing that would guarantee that this particular address is valid (and that it is writable) within your process memory space! If fact, with such an arbitrarily chosen address, chances are extremely high that the address happens to not be valid 😨

Also, because of things like ASLR, the memory layout of your program may be different on each execution:
https://en.wikipedia.org/wiki/Address_space_layout_randomization

Last but not least, you can not access the memory of another process, because the (virtual) memory spaces of each process is totally separate from other processes. The very same address that is valid in one process can have a completely different meaning, or can be "invalid", in the other process. If processes want to share data via the memory, you explicitly have to create a "shared memory" segment and then each of the processes needs to map that "shared memory" into its own address space. Here is a tutorial for Windows:
https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory


(If you think of virtual memory addresses as telephone numbers, then the "space" of possible telephone numbers is huge, but most of those numbers have not been assigned to customers yet. Trying to call an "unassigned" number will give you a "this number is currently unavailable" announcement. The access violation is the operating system's way of telling you that you called an unassigned number 😏)
Last edited on
kigar64551
Thank you really much. I got it well with enough details. :)
My guess was also correct likely when I said the memory block of that address is not already allocated so it may not be possible to assign to it.
Visual Studio:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <cstdint>

template < typename T > std::uintptr_t iaddress_of( T& obj )
{ return reinterpret_cast<std::uintptr_t>( std::addressof(obj) ) ; }

template < typename T > bool writeable( std::uintptr_t address )
{
    bool result = true ;
    char* ptr = reinterpret_cast<char*>(address) ;

    __try 
    { 
        for( std::size_t i = 0 ; i < sizeof( T ) ; ++i )
        {
            const char c = ptr[i] ;
            ptr[i] = 0 ;
            ptr[i] = c ;
        }
    }
    __except(1) { result = false ; }

    return result ;
}

int main()
{
    int i = 34 ;
    std::cout << std::boolalpha << "writeable<int>( iaddress_of(i) ): " 
              << writeable<int>( iaddress_of(i) ) << '\n' ; // true

    static const int c = 9 ;
    std::cout << std::boolalpha << "writeable<int>( iaddress_of(c) ): " 
              << writeable<int>( iaddress_of(c) ) << '\n' ; // false

    std::cout << std::boolalpha << "writeable<int>( 0x25D4C3FA ): "
              << writeable<int>( 0x25D4C3FA ) << '\n' ; // ????

    std::cout << std::boolalpha << "writeable<double>( iaddress_of(i) ): "
              << writeable<double>( iaddress_of(i) ) << '\n' ; // ????
}
@JLBorges
I tried but couldn't get well what your code does exactly. Could you please explain it in a simple language. Maybe after that I will try again.

@seeplus: Thanks for the link. Do you have a platform-independent link to suggest about C++ memory management with a little details on what's going on under the hood when working with the memory?

1
2
int i{5};
std::cout << &a;
Can we be sure that that address belongs to actual memory (RAM) or may it be an address on the virtual memory?
Last edited on
Memory management 'under the hood' is os specific. C++ just calls as appropriate the required os api calls.

For an os like Windows, it's a virtual address. Every process running has it's own virtual address space with the same memory address range. The os brings required virtual memory into real memory as/when required. As a user program, you know nothing about that. To deal with actual memory you need to be writing 'kernel code' using Windows Driver Kit (WDK)- which is a different beast entirely.
> Could you please explain it in a simple language.

With comments added:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// return true if sizeof(T) bytes starting at the byte at the given virtual address is writeable
// pedantically, T should be a trivially copyable type 
// note: if mutiple threads are involved, another thread may change the process's access permissions to the memory being tested
//       concurrently or after this function has returned. in that sense,  this function may not thread safe
template < typename T > bool writeable( std::uintptr_t address )
{
    bool result = true ;
    char* ptr = reinterpret_cast<char*>(address) ; // to access memory at the virtual address as a sequence of bytes

    // try to write to sizeof(T) bytes from the address ptr (from within a guarded block)
    __try 
    { 
        for( std::size_t i = 0 ; i < sizeof(T) ; ++i )
        {
            // if there is an attempt to acess the virtual memory incorrectly 
            // (for example try to write to memory for which there is no write access)
            // an exception (typicaly EXCEPTION_ACCESS_VIOLATION) would be raised
            const char c = ptr[i] ; // save the current value of the byte
            ptr[i] = 0 ; // try to write to this byte
            ptr[i] = c ; // restore the saved value
        }
    }

    // if an exception was raised, transfer control to the exception handler
    // note: the filter expression is 1 (1 == EXCEPTION_EXECUTE_HANDLER)
    __except(1) { result = false ; } // the handler sets result to false 

    return result ; // result would be true if no exception was raised, false otherwise
}
About @JLBorges code these "exceptions" are Windows "structured exception handling" (SEH) exceptions, not standard C++ exceptions. SEH is a language extension.
https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-170

Do you have a platform-independent link to suggest about C++ memory management with a little details on what's going on under the hood when working with the memory?

See Ulrich Drepper What Every Programmer Should Know About Memory:
https://www.akkadia.org/drepper/cpumemory.pdf
Last edited on
Do you find that pdf still valid (although it's of 2007)?
Mostly. Details change (e.g., we're not using DDR2 any more) but the operating principles remain mostly the same.

You could get the 6th (2019) edition of Hennessy and Patterson Computer Architecture: A Quantitative Approach. Although it's not really comparable to Drepper's essay (because their emphasis is different), they do spend a long time deeply discussing memory.
Last edited on
Thanks for the link. Do you have a platform-independent link to suggest about C++ memory management with a little details on what's going on under the hood when working with the memory?

C++ operators like new, new[], delete and delete[] usually use malloc() and free() under the hood. But, of course, they do more than just calling malloc() and free(), e.g. they also call the constructor or destructor of the object to be created/destoryed. But, the actual memory allocation (or de-allocation) usually comes down to malloc() and free(), even in C++ code.

malloc() is implemented by the C-Runtime. It can be implemented in many different ways! Ultimately, the C-Runtime has to resort to operating system functions (syscalls) in order to allocate memory pages from the operating system. But not every malloc() invocation necessarily requires allocating memory from the operating system! Usually, the C-Runtime maintains a "pool" of memory pages that it can use to serve malloc() requests. This can be done by the C-Runtime alone, without bothering the operating system. Only if that "pool" is completely used up (or too much fragmented), the C-Runtime is going to allocate "fresh" memory pages from the operating system.

(in reality, the details of malloc() are more complicated, as it is highly optimized)


Memory pages are allocated from the operating-system, e.g., by using the mmap() system call:
https://en.wikipedia.org/wiki/Mmap

Details on the malloc() implementation of glibc (GNU C library), as used on most Linux systems, can be found here:
https://sourceware.org/glibc/wiki/MallocInternals

On Windows, malloc() usually comes down to a simple HeapAlloc():
https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc


Can we be sure that that address belongs to actual memory (RAM) or may it be an address on the virtual memory?

Unless you are writing kernel code or device drivers, you are dealing exclusively with "virtual" addresses! So, there is no distinction between "virtual" and "physical" addresses, as far as "normal" application code is concerned – in modern operating systems.

Everything that you are going to be concerned with, in your application code, is "virtual" addresses.

Note that the "virtual" addresses that you have allocated usually are backed by "physical" RAM, but normally you never get to know those "physical" addresses. In fact, the "physical" addresses may change over time, when the operating system moves around pages in the RAM; your application doesn't need to worry about that! Last but not least, the operating system may even move memory pages to the "swap file" on your HDD/SDD, e.g. in "low RAM" situations. Again, your application doesn't and shouldn't need to know about this...

(if you application tries to access a memory page that has been moved to the "swap file", the operating system intercepts that memory access, loads the desired memory page back into the RAM, and then allows your application to proceed "normally")

It's the MMU that translates "virtual" addresses into "physical" addresses, but this happens transparently to the application code:
https://en.wikipedia.org/wiki/Memory_management_unit#/media/File:MMU_principle_updated.png
Last edited on
Topic archived. No new replies allowed.