简体   繁体   中英

64-bit subtraction result to 32-bit integer

There is an existing function named "Compare," which is

int compare(void* A, void* B) { return (int)A - (int)B; }

I am aware that this is an atrocious practice, but I did not write that piece of code and it is being used in many places already. But this code was generating a compilation error under 64-bit, since void* is no longer 32-bit, so I fixed the code to the following.

int compare(void* A, void* B) { return (long long)A - (long long)B; }

What is the likelihood of this function returning an incorrect result under current 64-bit Linux architecture? ie, what is the likelihood of two virtual addresses being apart for more than 0x7FFFFFFF?

我想你想要的

int compare(void* A, void* B) { return (A > B) - (A < B); }

On my linux machine, here's an example.

I have a copy of tcsh running. It has a process id of 9732. We can look at the memory maps of it by examining /proc/<pid>/maps .

From the table below, we can see that heap data is stored around 0x01e30000 , while stack data is stored around 0x7fffca3e6000 . So in a simple case, if you compare a malloc allocated pointer with a stack pointer, you'll see a significant difference in pointers.

[1:02pm][wlynch@charcoal Harrow] cat /proc/9732/maps
00400000-0045a000 r-xp 00000000 09:00 44826689                           /bin/tcsh
0065a000-0065e000 rw-p 0005a000 09:00 44826689                           /bin/tcsh
0065e000-00674000 rw-p 00000000 00:00 0 
0085d000-0085f000 rw-p 0005d000 09:00 44826689                           /bin/tcsh
01e30000-01f78000 rw-p 00000000 00:00 0                                  [heap]
38a3c00000-38a3c1e000 r-xp 00000000 09:00 16253177                       /lib64/ld-2.12.so
38a3e1e000-38a3e1f000 r--p 0001e000 09:00 16253177                       /lib64/ld-2.12.so
38a3e1f000-38a3e20000 rw-p 0001f000 09:00 16253177                       /lib64/ld-2.12.so
38a3e20000-38a3e21000 rw-p 00000000 00:00 0 
38a4400000-38a4575000 r-xp 00000000 09:00 16253179                       /lib64/libc-2.12.so
38a4575000-38a4775000 ---p 00175000 09:00 16253179                       /lib64/libc-2.12.so
38a4775000-38a4779000 r--p 00175000 09:00 16253179                       /lib64/libc-2.12.so
38a4779000-38a477a000 rw-p 00179000 09:00 16253179                       /lib64/libc-2.12.so
38a477a000-38a477f000 rw-p 00000000 00:00 0 
38a4800000-38a4802000 r-xp 00000000 09:00 16253186                       /lib64/libdl-2.12.so
38a4802000-38a4a02000 ---p 00002000 09:00 16253186                       /lib64/libdl-2.12.so
38a4a02000-38a4a03000 r--p 00002000 09:00 16253186                       /lib64/libdl-2.12.so
38a4a03000-38a4a04000 rw-p 00003000 09:00 16253186                       /lib64/libdl-2.12.so
38af000000-38af01d000 r-xp 00000000 09:00 16253156                       /lib64/libtinfo.so.5.7
38af01d000-38af21d000 ---p 0001d000 09:00 16253156                       /lib64/libtinfo.so.5.7
38af21d000-38af221000 rw-p 0001d000 09:00 16253156                       /lib64/libtinfo.so.5.7
38b0c00000-38b0c58000 r-xp 00000000 09:00 16253191                       /lib64/libfreebl3.so
38b0c58000-38b0e57000 ---p 00058000 09:00 16253191                       /lib64/libfreebl3.so
38b0e57000-38b0e59000 rw-p 00057000 09:00 16253191                       /lib64/libfreebl3.so
38b0e59000-38b0e5d000 rw-p 00000000 00:00 0 
38b1000000-38b1007000 r-xp 00000000 09:00 16253192                       /lib64/libcrypt-2.12.so
38b1007000-38b1207000 ---p 00007000 09:00 16253192                       /lib64/libcrypt-2.12.so
38b1207000-38b1208000 r--p 00007000 09:00 16253192                       /lib64/libcrypt-2.12.so
38b1208000-38b1209000 rw-p 00008000 09:00 16253192                       /lib64/libcrypt-2.12.so
38b1209000-38b1237000 rw-p 00000000 00:00 0 
7f03aa9a0000-7f03aa9ac000 r-xp 00000000 09:00 16252957                   /lib64/libnss_files-2.12.so
7f03aa9ac000-7f03aabab000 ---p 0000c000 09:00 16252957                   /lib64/libnss_files-2.12.so
7f03aabab000-7f03aabac000 r--p 0000b000 09:00 16252957                   /lib64/libnss_files-2.12.so
7f03aabac000-7f03aabad000 rw-p 0000c000 09:00 16252957                   /lib64/libnss_files-2.12.so
7f03aabbc000-7f03aabc3000 r--s 00000000 09:00 5769665                    /usr/lib64/gconv/gconv-modules.cache
7f03aabc3000-7f03b0a54000 r--p 00000000 09:00 5506757                    /usr/lib/locale/locale-archive
7f03b0a54000-7f03b0a58000 rw-p 00000000 00:00 0 
7f03b0a5b000-7f03b0a67000 r--p 00000000 09:00 5510943                    /usr/share/locale/en/LC_MESSAGES/tcsh
7f03b0a67000-7f03b0a68000 rw-p 00000000 00:00 0 
7fffca3e6000-7fffca3fb000 rw-p 00000000 00:00 0                          [stack]
7fffca3ff000-7fffca400000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

This looks like a sort comparison function, so all that matters is the sign of the result.

int compare(void *A, void* B)
{
  if (A < B) return -1;
  if (A > B) return 1;
  return 0;
 }

You shouldn't be returning an int, but rather a uintptr_t. Make sure variables set to this function are uintptr_t too.

uintptr_t compare(void* A, void* B) { return (uintptr_t)A - (uintptr_t)B; }

If you can't do this for whatever reason, then you should be checking that the values are within range and throw an error if not. The chance is probably very small, but that doesn't mean it's an exceptional case.

int compare (void*p, void*q) { return (p==q)?0:((char*)p < (char*)q)?-1:1; }

If you happen to compare addresses on the stack and heap I would say it's quite possible that the difference could be larger than that (since typically the heap grows up from the bottom and the stack grows down from the top).

Instead of returning int , return ptrdiff_t which as its name implies is an integral type big enough to hold pointer differences. You do still have to cast, although I chose char* instead of int because it lets you use static_cast :

ptrdiff_t compare(void* A, void* B) { return static_cast<char*>(A) - static_cast<char*>(B); }

Finally if you're using this with C's qsort , just take the simple way: Use std::sort which can use a single < and doesn't need to do any subtraction at all!

First of all: Pointer comparisons are only defined if the pointers point into the same array. But I assume you don't care as long as it works in practice.

On x86 and AMD64 comparisons between pointers with < and > will most likely work in practice. But difference based methods can fail due to int-overflows.

There is an existing function named "Compare," which is

 int compare(void* A, void* B) { return (int)A - (int)B; } 

This is already broken on 32 bit systems where pointers the high bit set exist. This will not only lead to a strange ordering, but violates the transitivity property an order needs. On windows this can happen with applications that use /LARGEADDRESSAWARE (which allows 3GB of user mode address space). I don't know enough about Linux to know if it can happen there.

So you should use Ben Voigt's code even on 32 bit systems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM