How to know if a pointer points to dynamically allocated memory or not?

So I have searched on the internet and found that there is no way to find out if a pointer points to dynamically allocated memory or not. The C++ standard itself is silent on this matter. There are however certain "extensions" that exist that help the solve the problem "partially". I have not looked into all of them yet.

C++ book tells me that variables are assigned on stack and heap. Static variables are on stack and dynamic ones are in the heap. Memory in the heap can be allocated and freed as needed. Now shouldn't it be that certain range of addresses for a program is assigned as stack and another chunk as the heap. It should then be possible to get the start and end addresses of these memory ranges. Now we only need to see where the address in pointer lies and we know if it points to dynamically allocated memory or static one.

Why is it not that simple?
Why would you want to know?
This is the way I think about it, probably not 100% correct in all cases but the basic concept.

When you code and declare a variable (examples: int or string) or array, that is static memory and the size can't be changed while the program is running. That's why INT for example has a fixed size. Everything else is pretty much dynamic. One example is a vector.

http://www.cs.fsu.edu/~myers/c++/notes/dma.html


Edit. You can not free static memory while the program is running.

It should then be possible to get the start and end addresses of these memory ranges.

True, you can

Now we only need to see where the address in pointer lies and we know if it points to dynamically allocated memory or static one.

False
For one thing memory use is not always going to be sequential.

When you look at the memory address it's just going to tell you what's stored in it. I don't know how you tell what type it is unless you know what data should be there. There are probably some memory tools out there to give you this information. Static memory should be easier to identify since you should know what's going there but it may be converted.

Now obviously if you can free it, then it's not static.
Last edited on
Now shouldn't it be that certain range of addresses for a program is assigned as stack and another chunk as the heap.


sure, you can do that by examining the process memory map, here's how it works on Linux:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <array>
#include <vector>
#include <iostream>
#include <fstream>

int main()
{
    std::array<int, 1000> a;
    static std::array<int, 1000> b;
    std::vector<int> v(10000);

    std::cout << "stack pointer:  " << a.data() << '\n'
              << "static pointer: " << b.data() << '\n'
              << "heap pointer:   " << v.data() << '\n';

    std::cout << "Process memory map: " << '\n'
              << std::ifstream("/proc/self/maps").rdbuf() << '\n';
}


live demo: http://melpon.org/wandbox/permlink/LgFAmSpWOGGzhG7Z

stack pointer:  0x7fffe208a6a0
static pointer: 0x602440
heap pointer:   0x848c20
Process memory map: 
00400000-00402000 r-xp 00000000 fd:01 742273                             /home/jail/prog.exe
00602000-00603000 rw-p 00002000 fd:01 742273                             /home/jail/prog.exe
00603000-00604000 rw-p 00000000 00:00 0 
00837000-00869000 rw-p 00000000 00:00 0                                  [heap]
7f4a670dd000-7f4a67291000 r-xp 00000000 fd:01 11984791                   /lib/x86_64-linux-gnu/libc-2.15.so
7f4a67291000-7f4a67490000 ---p 001b4000 fd:01 11984791                   /lib/x86_64-linux-gnu/libc-2.15.so
7f4a67490000-7f4a67494000 r--p 001b3000 fd:01 11984791                   /lib/x86_64-linux-gnu/libc-2.15.so
7f4a67494000-7f4a67496000 rw-p 001b7000 fd:01 11984791                   /lib/x86_64-linux-gnu/libc-2.15.so
7f4a67496000-7f4a6749b000 rw-p 00000000 00:00 0 
7f4a6749b000-7f4a674b1000 r-xp 00000000 fd:01 5258485                    /usr/local/gcc-6.1.0/lib64/libgcc_s.so.1
7f4a674b1000-7f4a676b0000 ---p 00016000 fd:01 5258485                    /usr/local/gcc-6.1.0/lib64/libgcc_s.so.1
7f4a676b0000-7f4a676b1000 rw-p 00015000 fd:01 5258485                    /usr/local/gcc-6.1.0/lib64/libgcc_s.so.1
7f4a676b1000-7f4a677ac000 r-xp 00000000 fd:01 11985009                   /lib/x86_64-linux-gnu/libm-2.15.so
7f4a677ac000-7f4a679ab000 ---p 000fb000 fd:01 11985009                   /lib/x86_64-linux-gnu/libm-2.15.so
7f4a679ab000-7f4a679ac000 r--p 000fa000 fd:01 11985009                   /lib/x86_64-linux-gnu/libm-2.15.so
7f4a679ac000-7f4a679ad000 rw-p 000fb000 fd:01 11985009                   /lib/x86_64-linux-gnu/libm-2.15.so
7f4a679ad000-7f4a67b1f000 r-xp 00000000 fd:01 5259277                    /usr/local/gcc-6.1.0/lib64/libstdc++.so.6.0.22
7f4a67b1f000-7f4a67d1e000 ---p 00172000 fd:01 5259277                    /usr/local/gcc-6.1.0/lib64/libstdc++.so.6.0.22
7f4a67d1e000-7f4a67d28000 r--p 00171000 fd:01 5259277                    /usr/local/gcc-6.1.0/lib64/libstdc++.so.6.0.22
7f4a67d28000-7f4a67d2a000 rw-p 0017b000 fd:01 5259277                    /usr/local/gcc-6.1.0/lib64/libstdc++.so.6.0.22
7f4a67d2a000-7f4a67d2e000 rw-p 00000000 00:00 0 
7f4a67d2e000-7f4a67d46000 r-xp 00000000 fd:01 11984842                   /lib/x86_64-linux-gnu/libpthread-2.15.so
7f4a67d46000-7f4a67f45000 ---p 00018000 fd:01 11984842                   /lib/x86_64-linux-gnu/libpthread-2.15.so
7f4a67f45000-7f4a67f46000 r--p 00017000 fd:01 11984842                   /lib/x86_64-linux-gnu/libpthread-2.15.so
7f4a67f46000-7f4a67f47000 rw-p 00018000 fd:01 11984842                   /lib/x86_64-linux-gnu/libpthread-2.15.so
7f4a67f47000-7f4a67f4b000 rw-p 00000000 00:00 0 
7f4a67f4b000-7f4a67f6d000 r-xp 00000000 fd:01 11985006                   /lib/x86_64-linux-gnu/ld-2.15.so
7f4a68157000-7f4a6815d000 rw-p 00000000 00:00 0 
7f4a6816a000-7f4a6816d000 rw-p 00000000 00:00 0 
7f4a6816d000-7f4a6816e000 r--p 00022000 fd:01 11985006                   /lib/x86_64-linux-gnu/ld-2.15.so
7f4a6816e000-7f4a68170000 rw-p 00023000 fd:01 11985006                   /lib/x86_64-linux-gnu/ld-2.15.so
7fffe206b000-7fffe208c000 rw-p 00000000 00:00 0                          [stack]
7fffe21d5000-7fffe21d7000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]


as you can see, my heap pointer 0x848c20 is in the heap region 00837000-00869000

but what are you going to do with that information?
Last edited on
Now we only need to see where the address in pointer lies and we know if it points to dynamically allocated memory or static one.


Well, you should check to see if the pointer value corresponds to the the stack not. If it is not, then it can be assumed to be dynamic memory. You can get the current stack-size and starting address (on x86, at least) from the ESP and EBP processor registers.

The stack is sequential -- I'm not 100% if that is required, but it is on every platform I've ever used.
Similarly, I don't know if memory addresses are required to be unique (no overlap) but I would have a hard time supporting the case where they weren't.

Regardless, you can't do this in standard C++. Why would you need to know?
Last edited on
The stack is sequential -- I'm not 100% if that is required, but it is on every platform I've ever used.
Both GNU and Clang allow segmented stacks, and of course every thread has its own stack. A program that uses coroutines may perform user-space context switches, after which the stack pointer may point into a dynamically allocated region.
Literal constants have a corner of their own too, don't they?
String literals of course are allocated along with other static objects. Numeric literals can usually be encoded in the opcodes, depending on the value and the instruction set. Also, if the address of a numeric symbolic constant is never obtained, the compiler may treat it as if it was a literal.
@helios

Both GNU and Clang allow segmented stacks

Hmm -- good to know. But when would that feature be used, and why? Just a space optimization, I guess?

A program that uses coroutines may perform user-space context switches, after which the stack pointer may point into a dynamically allocated region.

Can you explain how coroutines might leave the stack pointer referring to dynamically-allocated memory? Obviously the current context has to be saved somewhere (be it in dynamic memory or not) so that it can be restored at a later point, but that doesn't explain how the stack pointer might refer to memory elsewhere.

Edit:
I found some information about segmented stacks on the LLVM site.
http://llvm.org/releases/3.0/docs/SegmentedStacks.html

The page points out that in the case of a segmented stack, new blocks of stack (the page calls them "stacklets") might be dynamically allocated as required (it would defeat the point if the memory was already available). That answers both my questions.
:)
Last edited on
Topic archived. No new replies allowed.