Tutorial :The bounds on void-pointers in ANSI C89/ISO C90



Question:

Is there a way to portably determine the upper and lower bound on void-pointer values in ANSI C89/ISO C90? (I currently do not have a copy of the standard with me (I have one at home). Of course if void-pointer values are guaranteed to be unsigned this task is trivial (via sizeof(void *)); however, I cannot recall if this is guaranteed or not. I can think of a few very inefficient algorithms (increment until overflow, etc.), but I would like to know if anyone has a relatively cheap (in terms of time-complexity) and portable way to calculate these bounds.)

--EDIT--

Also: Is there a portable way to determine the validity of the pointer values?

Why: This came up in a discussion with a co-worker and it stumped me. I don't know what he's working on, but I just want to know because I am interested! :-)


Solution:1

There is no portable way to determine if a given pointer is valid or not. You have to know what kind of memory system you're dealing with. Depending on the operating system and processor, there may or may not be a way to query the virtual memory manager's page tables to determine the valid ranges of pointers.

For example, on Linux, you can examine the special mmap file under /proc to get the virtual memory map of a process. Here's an example of cat reading out its own memory map:

  $ cat /proc/self/mmap  08048000-0804c000 r-xp 00000000 09:00 5128276                            /bin/cat  0804c000-0804d000 rw-p 00003000 09:00 5128276                            /bin/cat  0804d000-0806e000 rw-p 0804d000 00:00 0                                  [heap]  f7ca7000-f7e40000 r--p 00000000 09:00 3409654                            /usr/lib/locale/locale-archive  f7e40000-f7e41000 rw-p f7e40000 00:00 0   f7e41000-f7f68000 r-xp 00000000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so  f7f68000-f7f6d000 r--p 00127000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so  f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so  f7f6f000-f7f72000 rw-p f7f6f000 00:00 0   f7f83000-f7f85000 rw-p f7f83000 00:00 0   f7f85000-f7f9a000 r-xp 00000000 09:00 2637871                            /lib/ld-2.3.6.so  f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871                            /lib/ld-2.3.6.so  ff821000-ff836000 rw-p 7ffffffea000 00:00 0                              [stack]  ffffe000-fffff000 r-xp ffffe000 00:00 0                                  [vdso]  

You can see the ranges of valid pointers, along with the bits indicating if the memory is (r)eadable, (w)ritable, e(x)ecutable, or (p)resent (i.e. not paged out to disk).


Solution:2

Pointers are guaranteed by spec to be unsigned. But why on earth do you want to find the bounds? "Everything between 0x00000001 and 0xffffffff" is not really a useful test, since the number of valid pointers will be some tiny subset of that.


Solution:3

void * is always big enough to hold a pointer to addressable memory. Any other use is strictly prohibited by the major league baseball association.

Example: The dec-10 was a 36 bit architecture with 36 bit words. Yet the addresses were 18 bits and you could hold 2 pointers in any register/word.

Yeah - that's an extreme example. If you must do math with pointers, sizeof is valid; but doing pointer math on anything other than a contiguous array is dodgier than dodgy.

Finally - never use a 'void *' to store a pointer to an object or pointer to member in C++. Many compiler implementation actually use multiple 'physical' pointers to implement multiple inheritence of concrete (or partially concrete) classes. In reality this almost never comes up because very few people use multiple inheritance in this way, and when they do, very rarely slice and unslice pointers. When it does come up, it's really hard to figure out what happened.


Solution:4

You have to discern the integer value to which a void * can be cast from the actual bit pattern in memory - casting a void * to an integer type may involve conversions!

Assuming sizeof(void *) == sizeof(long), for a void * p the following may well be false:

((long)p) == *((long *)&p)  

Also, the standard doesn't specify whether there even is an integer type large enough to hold the values of all valid pointers!

Therefore, there just is no portable way to do what you want to do...


Solution:5

Aside from a region that corresponds to NULL, there is no (portable) restriction on the memory addresses at all. A sufficiently hardened OS could utilize various CPU/OS mechanisms to supply each process with random and well-distributed addresses with each call to malloc(), and position independent executable plus ASLR can allow code to run from any address as well.


Solution:6

I know that on Win32, 64-bit pointers are sign-extended. It's fun to inspect a 32-bit minidump from a 64-bit machine if you don't sign extend pointers.

See here for how a 64-bit pointer (POINTER_64) works on Win32.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »