64-битті шығарып алу 32-биттік бүтін санға әкеледі

Бар функция «Салыстыру» деп аталады

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-ден артық екі виртуалды адресті бөліп шығару ықтималдығы қандай?

3
Әрине, ықтималдық 1/2. Сіз 32-биттік және 64-биттік жүйелерде де сенімді нәтижелерге ие боласыз, егер сіз оларды бір-біріне немесе басқаларға айналдыруға тырыспастан, тікелей екі көрсеткіштің арасындағы айырмашылықты алсаңыз. А айырмашылығы (void *) A - (void *) B оңай реттеуге болатын ptrdiff_t түрінің айырмашылығын береді.
қосылды автор Pete Wilson, көзі
@BenVoigt: D'oh, сіз дұрыссыз. (char *) А - (char *) B жұмыс істейді.
қосылды автор Keith Thompson, көзі
Бұл функция қалай пайдаланылады? Егер ол qsort() үшін салыстыру функциясы ретінде пайдаланылса, ол дұрыс жұмыс істемейді. Егер болмаса, көрсеткішті кез келген бүтін түріне түрлендіру мағынасы жоқ; тікелей көрсеткішті алып тастаңыз (ол ptrdiff_t ).
қосылды автор Keith Thompson, көзі
@KeithThompson: Сіз көрсеткіштерді толық емес түрге шығара алмайсыз.
қосылды автор Ben Voigt, көзі
long орнына uintptr_t пайдалануды ұсынамын.
қосылды автор Bill Lynch, көзі
«Теріс тәжірибе» - Мен қатты келіспеймін. Кейде бұл «қырылған» - конструкциялармен қажет. Бұл дінге ұқсайды: сендердің православиелерің мен күнделікті өмірге баруға тырысатындар бар. Православие сіз үшін бірдеңе жасамаудың себептерін келтіре алады және егер олар балама ұсынса, сіздің өміріңізге (осы жағдайда жұмыс істеуге) практикалық тәсілмен араласуға тырысуыңыз мүмкін .
қосылды автор Olof Forshell, көзі
өте қайғылы тұлға :(
қосылды автор user166390, көзі

7 жауаптар

Менің ойымша, сіз келеді

int compare(void* A, void* B) { return (A > B) - (A < B); }
6
қосылды
Айырмашылыққа негізделген кодынан айырмашылығы, бұл ішкі ағымдық мәселелерден зардап шекпейді. Әрине, бұл әлі анықталмаған мінез-құлық, бірақ бұл ОС талаптарына тән.
қосылды автор CodesInChaos, көзі

Менің linux компьютерімде мысал келтірейік.

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//maps.

Төмендегі кестеден үй деректерін 0x01e30000 қаптамасында сақтайтындығын көруге болады, ал stack деректері 0x7fffca3e6000 ішінде сақталады. Осылайша қарапайым жағдайда мульти бөлінген көрсеткішті стек көрсеткішімен салыстырсаңыз, көрсеткіштерде айтарлықтай айырмашылықты көресіз.

[1:02pm][[email protected] 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]
3
қосылды
Көрсеткіштерді нақты нысандарға (мысалы, біреудің стек объектісіне және біреуін үйме нысанына) салыстыру немесе салыстыру анықталмаған. Көптеген жүйелерде онымен бірге кетуіңіз мүмкін.
қосылды автор Keith Thompson, көзі
OTOH, бірдей композиттік нысанның бөлігі болып табылмайтын көрсеткіштерді салыстыру, бәрібір белгісіз әрекетке әкеледі.
қосылды автор Ben Voigt, көзі
+1 Бұл мәселеге нақты жауап.
қосылды автор Pubby, көзі

Бұл сортты салыстыру функциясына ұқсайды, сондықтан бәрі маңызды - нәтиженің белгісі.

int compare(void *A, void* B)
{
  if (A < B) return -1;
  if (A > B) return 1;
  return 0;
 }
2
қосылды
@BenVoigt: Жақсы, бірақ qsort салыстыру функциясының аргументтері олардың мәндерін емес, элементтердің мекен-жайлары болып табылады.
қосылды автор Keith Thompson, көзі
Бұл, әрине, көрінеді - бірақ егер адрестерді салыстыру болса, онда qsort() үшін салыстыру функциясы ретінде мағынасы жоқ. qsort() мекенжай бойынша сұрыпталған қазірдің өзінде элементтері бар массаны сұрыптайды. Тіпті егер бұлай болмаса да, айырбастау элементтері олардың мекен-жайларын өзгертіп, салыстыру нәтижелерін сәйкес келмейді.
қосылды автор Keith Thompson, көзі
@KeithThompson: Мүмкін сізге сұрыпталған көрсеткіш жинағы (көрсеткіш элемент емес, элементтің мекенжайы). Немесе сіз екілік іздеуді көрсеткіштердің сұрыпталған кестесінде жасауға тырысасыз.
қосылды автор Ben Voigt, көзі
@KeithThompson: Сіз тек qsort туралы ештеңе айта алмайсыз.
қосылды автор Ben Voigt, көзі

Сіз int-ты қайтармауыңыз керек, керісінше uintptr_t. Осы функцияға айнымалы мәндер орнатылғанын тексеріңіз uintptr_t.

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

Егер сіз мұны қандай да бір себептермен жасай алмасаңыз, онда маңызы ауқымның ішінде екенін тексеріп, қателесеңіз, қателік жіберіңіз. Мүмкіндік өте аз, бірақ бұл ерекше жағдай емес.

1
қосылды
size_t сілтегішті ұстап тұруға кепілдік бермейді. Ол шынымен size_t оралуы керек, бірақ тастаңыз басқа нәрсе болуы керек.
қосылды автор Paul Manta, көзі
... әрине char * түрлендіргеннен кейін, әрине.
қосылды автор Keith Thompson, көзі
@BenVoigt: Сіз дұрыссыз. Меніңше кофе жеткіліксіз.
қосылды автор Keith Thompson, көзі
intptr_t -ге ауыспаңыз. Көрсеткіштерді өздері салыстырыңыз.
қосылды автор Keith Thompson, көзі
@KeithThompson: Тек сіз салыстырып жатсаңыз, түрлендірудің қажеті жоқ. Тек көрсеткіш арифметикасы үшін ( sizeof (element_type) бойынша масштабтау қажет).
қосылды автор Ben Voigt, көзі
Мен бірдеңе жетіспеймін бе, әлде бұл функция мүлдем естімесіз бе? Нәтиже әрдайым> = 0 болып табылады және сәйкесінше белгісі (Салыстыру (a, b)) = - белгісі (Салыстыру (b, a)) талаптарын бұзады. .
қосылды автор CodesInChaos, көзі
@ Pubby8: көрсеткішті ұстап тұру үшін uintptr_t »анықталды.
қосылды автор Bill Lynch, көзі
@PaulManta uintptr_t болса керек, дұрыс?
қосылды автор Pubby, көзі
Үш жолды салыстыру функцияларының мәні аргументтерді реттеуге байланысты теріс, нөлдік немесе оң мәнді қайтару болып табылады. Егер uintptr_t сияқты белгісіз түрін қайтарсаңыз, сіз теріс мәнді ешқашан қайтармайсыз. Сонымен, сіз intptr_t алғыңыз келеді.
қосылды автор Richard Kettlewell, көзі
int compare (void*p, void*q) { return (p==q)?0:((char*)p < (char*)q)?-1:1; }
0
қосылды
Дәлірек айтқанда, дәл солай ұсынуға тура келді, сонда оны жазудың қарапайым тәсілі бар екенін түсінді.
қосылды автор Ben Voigt, көзі

Ең алдымен: көрсеткішін салыстырулар тек көрсеткіштер бірдей массивке меңзеген кезде ғана анықталады. Бірақ, іс жүзінде жұмыс істейтіндіктен, сізге ешқандай да көңіл бөлмейміз деп ойлаймын.

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.

«Салыстыру» деп аталатын бұрыннан бар функция бар, ол

 int салыстыру (void * A, void * B) {return (int) A - (int) B; }
 

Бұл 32 биттік жүйелерде жоғары бит белгілері бар нүктелерде сынған. Бұл тек біртүрлі тапсырыс беруді ғана емес, сонымен қатар транзитивтік қасиетті тәртіпті бұзады. Терезелерде бұл /LARGEADDRESSAWARE қолданатын қосымшалармен (ол 3 Гб пайдаланушы режимінің мекенжай кеңістігіне мүмкіндік беретін) болуы мүмкін. Мен Linux туралы жеткілікті білмеймін, сонда ол орын алуы мүмкін.

Сондықтан Ben Voigt кодты 32 бит жүйелерінде де пайдалануыңыз керек.

0
қосылды

Егер сіз мекенжайларды стак пен үймедегі салыстыруды байқасаңыз, айырмашылықтың үлкен болуы мүмкін (әдетте қабырға түбінен өсіп, үстінен үстіңнен төмендейді).

int қайтарудың орнына, ptrdiff_t деген атауды келтіріңіз, ол оның атауын білдіретін көрсеткіш айырмашылықтарын сақтау үшін жеткілікті үлкен интегралды түр болып табылады. static_cast функциясын қолдануға мүмкіндік беретін char орнына char орнына таңдап алсам да, әлі де тастауға тура келеді:

ptrdiff_t compare(void* A, void* B) { return static_cast(A) - static_cast(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!

0
қосылды
ptrdiff_t салыстыру (void * A, void * B) {return (char *) A - (char *) B; }
қосылды автор Keith Thompson, көзі
Көрсеткіштерді толық емес түрге шығара алмайсыз.
қосылды автор Ben Voigt, көзі
«Екі көрсеткішті алып тастау тек сол массивтің элементтеріне көрсеткіштер үшін дұрыс анықталған мәнге ие болады (немесе элемент үшін массивтің соңындағы соңғы).» Бұл барлық көрсеткіш салыстыру үшін орынды болса да, ол тікелей көрсеткішті салыстырудан гөрі іс жүзінде көп жағдайда сәтсіз болады.
қосылды автор CodesInChaos, көзі
@Ben Voigt char * айырмашылығын пайдалану үшін жаңартылды.
қосылды автор Mark B, көзі