Результат 64-разрядного вычитания в 32-разрядное целое число - PullRequest
3 голосов
/ 28 октября 2011

Существует существующая функция с именем "Сравнить", которая

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

Я знаю, что это ужасная практика, но я не писал этот кусок кода, и он уже используется во многих местах. Но этот код генерировал ошибку компиляции под 64-битной версией, поскольку void * больше не является 32-битной, поэтому я исправил код следующим образом.

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

Какова вероятность того, что эта функция выдаст неверный результат в текущей 64-битной архитектуре Linux? то есть, какова вероятность того, что два виртуальных адреса будут разделены более чем на 0x7FFFFFFF?

Ответы [ 7 ]

7 голосов
/ 28 октября 2011

Я думаю, что вы хотите

int compare(void* A, void* B) { return (A > B) - (A < B); }
3 голосов
/ 28 октября 2011

На моем компьютере с Linux, вот пример.

У меня запущена копия tcsh.У него есть идентификатор процесса 9732. Мы можем посмотреть на его карты памяти, изучив /proc/<pid>/maps.

. Из приведенной ниже таблицы видно, что данные кучи хранятся около 0x01e30000, а данные стекахранится около 0x7fffca3e6000.Так что в простом случае, если вы сравните указатель, выделенный malloc, с указателем стека, вы увидите существенную разницу в указателях.

[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]
2 голосов
/ 28 октября 2011

Это похоже на функцию сравнения сортировки, поэтому все, что имеет значение, это знак результата.

int compare(void *A, void* B)
{
  if (A < B) return -1;
  if (A > B) return 1;
  return 0;
 }
0 голосов
/ 29 октября 2011

Прежде всего: сравнения указателей определяются, только если указатели указывают на один и тот же массив. Но я полагаю, что вам все равно, пока это работает на практике.

На x86 и AMD64 сравнения указателей с < и >, скорее всего, будут работать на практике. Но методы, основанные на различиях, могут потерпеть неудачу из-за переполнения int.

Существует существующая функция с именем "Сравнить", которая

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

Это уже сломано в 32-битных системах, где существуют указатели старшего набора битов. Это не только приведет к странному упорядочению, но и нарушит свойство транзитивности, необходимое для заказа. В Windows это может происходить с приложениями, которые используют /LARGEADDRESSAWARE (что позволяет 3 ГБ адресного пространства пользовательского режима). Я не знаю достаточно о Linux, чтобы знать, может ли это произойти там.

Так что вы должны использовать код Бена Фойгта даже в 32-битных системах.

0 голосов
/ 28 октября 2011

Если вам случится сравнивать адреса в стеке и куче, я бы сказал, что вполне возможно, что разница может быть больше, чем эта (поскольку обычно куча растет снизу, а стек уменьшается сверху).

Вместо возврата int, верните ptrdiff_t, что, как следует из его названия, является целочисленным типом, достаточно большим для хранения различий указателей.Вы все еще должны разыграть, хотя я выбрал char* вместо int, потому что он позволяет вам использовать static_cast:

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

Наконец, если вы используете это с C qsort, простоВозьмите простой способ: используйте std::sort, который может использовать один < и вообще не нужно ничего вычитать!

0 голосов
/ 28 октября 2011
int compare (void*p, void*q) { return (p==q)?0:((char*)p < (char*)q)?-1:1; }
0 голосов
/ 28 октября 2011

Вы не должны возвращать int, а скорее uintptr_t.Убедитесь, что переменные, установленные для этой функции, тоже uintptr_t.

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

Если вы не можете сделать это по какой-либо причине, вам следует проверить, что значения находятся в пределах диапазона, и, если нет, выдать ошибку.Вероятность очень мала, но это не значит, что это исключительный случай.

...