Що таке тип даних uintptr_t


252

Що таке uintptr_tі для чого його можна використовувати?


5
Додаткову інформацію про цей тип, а також про інші споріднені типи можна знайти тут: opengroup.org/onlinepubs/000095399/basedefs/stdint.h.html
Недійсний

en.cppreference.com/w/cpp/types/integer списки std::uintptr_tта std::intptr_tдодатково C ++ 11.
alfC

Відповіді:


198

uintptr_tце цілочисельний тип без підпису, здатний зберігати покажчик даних. Що зазвичай означає, що він такого ж розміру, як і вказівник.

Це необов'язково визначено в C ++ 11 та пізніших стандартах.

Загальна причина, щоб хотіти цілочисельний тип, який може містити тип вказівника архітектури, - це виконувати цілочисельні операції над покажчиком або затемнити тип покажчика, надаючи його як цілочислену "ручку".

Редагувати: Зауважте, що у Стіва Джессопа є кілька дуже цікавих додаткових деталей (які я не буду красти) в іншій відповіді тут для вас педантичних типів :)


53
Зауважимо, що size_tпотрібно лише бути достатньою для розміру найбільшого розміру об'єкта і може бути меншою за покажчик. Цього можна було б очікувати на сегментованих архітектурах, таких як 8086 (16 біт size_t, але 32 біт void*)
MSalters

3
що представляє різницю між двома вказівниками, у вас є ptrdiff_t. uintptr_tне призначений для цього.
jalf

3
@jalf: Так, для різниці, але для відстані ви хочете непідписаний тип.
Дрю Дорман

Визначення uintptr_t, мабуть, не є обов'язковим (тому не є стандартним) навіть у C ++ 11! cplusplus.com/reference/cstdint (я отримав підказку від відповіді Стіва Джессопа)
Антоніо

2
@MarcusJ unsigned intзазвичай недостатньо великий. Але це може бути досить великим. Цей тип існує спеціально для видалення всіх "припущень" .
Дрю Дорманн

238

По-перше, на той момент питання uintptr_tбуло не в C ++. Це в C99, в <stdint.h>якості додаткового типу. Цей файл надає багато компіляторів C ++ 03. Він також знаходиться в C ++ 11, в <cstdint>, де знову це необов’язково, і який для визначення визначає C99.

У C99 він визначається як "цілий цільовий підпис без властивості, властивість якого будь-який дійсний вказівник на недійсну може бути перетворений у цей тип, потім перетворений назад у покажчик на недійсний, і результат порівняється рівним вихідному вказівнику".

Вживайте це, щоб означати, що воно говорить. Про розмір це нічого не говорить.

uintptr_tможе бути такого ж розміру, як і void*. Він може бути більшим. Він може бути меншим, хоча така реалізація C ++ підходить до викривлення. Наприклад, на якійсь гіпотетичній платформі, де void*є 32 біти, але використовується лише 24 біти віртуального адресного простору, у вас може бути 24-розрядний, uintptr_tякий задовольняє вимогу. Я не знаю, чому реалізація зробила б це, але стандарт це дозволяє.


8
Дякуємо за "<stdint.h>". Моя заявка не збиралася через декларацію uintptr_t. Але коли я читаю ваш коментар, я додаю "#include <stdint.h>", і так, це працює. Дякую!
JavaRunner

2
Для того, щоб виділити вирівняну пам'ять , серед інших застосувань, наприклад?
legends2k

4
Інший поширений випадок використання кидання покажчика на int - це створити непрозору ручку, яка приховує вказівник. Це корисно для повернення посилання на об'єкт з API, де ви хочете зберегти приватний об’єкт у бібліотеці та не допустити доступу програм до нього. Потім додаток змушений використовувати API для виконання будь-яких операцій над об'єктом
Джоел Каннінгем

3
@JoelCunningham: це працює, але насправді не відрізняється від використання void*. Це впливає на можливі майбутні напрямки, особливо, якщо ви хочете змінити, щоб використовувати щось, що насправді є лише цілою цілою ручкою, а не перетвореним покажчиком.
Стів Джессоп

2
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Загальний випадок використання - це передавати лише int API, який очікує недійсність * до загальних даних. Економить натискання клавіш typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. Замість цього: callback.dataPtr = (void*)val. З іншого боку, ви, звичайно, отримаєте void*і вам доведеться кинути його назад int.
Франческо Донді

19

Це непідписаний цілочисельний тип точно за розміром вказівника. Кожен раз, коли вам потрібно зробити щось незвичне з покажчиком - наприклад, інвертувати всі біти (не запитуйте, чому), ви кидаєте на нього uintptr_tта маніпулюєте ним як звичайне ціле число, а потім повертаєте назад.


6
Звичайно, ви могли це зробити, але це, звичайно, не визначена поведінка. Я вважаю, що єдине, що ви можете зробити з результатом передачі uintptr_t - це передати його без змін та відкинути її назад - все інше - це UB.
sleske

Бувають випадки, коли вам потрібно грати з бітами, і це, як правило, генерує помилки компілятора. Поширений приклад - використання 16-байтної вирівняної пам’яті для певних додатків для відео та критичних показників. stackoverflow.com/questions/227897/…
Хмара

3
@sleske це неправда. На машинах, які мають самовирівнені типи, два найменш значущих біта вказівника будуть дорівнювати нулю (оскільки адреси кратні 4 або 8). Я бачив програми, які використовують це для стискання даних ..
saadtaame

3
@saadtaame: Я просто вказав, що це UB в відповідності зі стандартом C . Це не означає, що це не може бути визначено в деяких системах - компілятори та режими виконання можуть вільно визначити конкретну поведінку для того, що є UB у стандарті C. Отже, немає протиріччя :-).
sleske

2
Не обов’язково точно розмір вказівника. Усі стандартні гарантії полягають у тому, що перетворення void*значення вказівника на uintptr_tзнову і назад дає void*значення, яке порівнюється з оригінальним вказівником. uintptr_tзазвичай має той самий розмір, що void*, але це не гарантується, і не існує жодної гарантії того, що біти перетвореного значення мають якесь особливе значення. І немає гарантії, що він може вмістити перетворене значення вказівника на функцію без втрати інформації. Нарешті, це не гарантується.
Кіт Томпсон

15

У частині "що таке тип даних uintptr_t" вже існує багато хороших відповідей. Я спробую розглянути питання про те, "для чого це можна використовувати?" участь у цій публікації.

В основному для побітових операцій на покажчиках. Пам'ятайте, що в C ++ ніхто не може виконувати побітові операції над покажчиками. Про причини див. Чому ви не можете робити побітові операції над покажчиком на C, і чи існує спосіб цього?

Таким чином, для того, щоб робити побітові операції на покажчиках, потрібно буде навести покажчики на тип unitpr_t, а потім виконати побітові операції.

Ось приклад функції, яку я щойно написав, щоб робити побітові ексклюзивні або 2 покажчики на зберігання у списку, пов’язаному з XOR, щоб ми могли переходити в обох напрямках, як подвійно пов'язаний список, але без штрафу зберігання 2 покажчиків у кожному вузлі .

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }

1
окрім бітної арифметики, це також приємно, якщо ви хочете мати семантику на основі адрес, а не підрахунок об'єктів.
Олексій

4

Рискуючи отримати ще один знак Necromancer, я хотів би додати одне дуже вдале використання для uintptr_t (або навіть intptr_t), і це написання тестового вбудованого коду. Я пишу здебільшого вбудований код, орієнтований на різні процесори та в даний час тенісилики. Вони мають різну ширину шини, і тенісиліка - це фактично гарвардська архітектура з окремими шинами коду та даних, які можуть бути різної ширини. Я використовую тестовий стиль розробки для більшої частини свого коду, що означає, що я роблю одиничні тести для всіх кодових одиниць, які я пишу. Тестування блоків на фактичному цільовому обладнанні - це клопот, тому я, як правило, записую все на ПК на базі Intel в Windows або Linux, використовуючи Ceedling та GCC. Зазначається, що багато вбудованого коду включає подвійне скручування та маніпуляції з адресою. Більшість моїх машин Intel 64-бітні. Отже, якщо ви збираєтеся перевірити код маніпуляції адресою, вам потрібен узагальнений об'єкт, щоб зробити математику. Таким чином, uintptr_t надає машинному незалежному способу налагодження коду перед тим, як спробувати розгорнути цільове обладнання. Інша проблема стосується деяких машин або навіть моделей пам'яті деяких компіляторів, функціональні покажчики та покажчики даних різної ширини. На цих машинах компілятор може навіть не допускати кастингу між двома класами, але uintptr_t повинен мати змогу утримувати будь-який.


Не впевнений, чи доречно ... Ви спробували "непрозорі типи"? Vide: youtube.com/watch?v=jLdSjh8oqmE
shycha

@sleske Я хотів би, щоб це було доступно в C. Але мати stdint.h - краще, ніж нічого. (Також хочу побажати, де сильніші набрані, але більшість налагоджувачів добре справляються з їх
виясненням
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.