без підпису int vs. size_t


492

Я зауважую, що сучасний код C і C ++, здається, використовується size_tзамість int/ unsigned intв значній мірі скрізь - від параметрів для C рядкових функцій до STL. Мені цікаво, що є причиною цього та користю, яку він приносить.

Відповіді:


388

size_tТип є цілим числом без знака тип , який є результатом sizeofоператора (і offsetofоператора), тому він гарантовано буде досить великим , щоб утримувати розмір найбільшого об'єкту система може обробляти (наприклад, статичний масив 8Gb).

size_tТипу може бути більше, дорівнює або менше , ніж unsigned int, і компілятор може робити припущення про це для оптимізації.

Більш точну інформацію ви можете знайти в стандарті C99, розділ 7.17, проект якого доступний в Інтернеті у форматі pdf , або в стандарті C11, розділ 7.19, також доступний у вигляді проекту PDF .


50
Ні. Подумайте про x86-16 з великою (не величезною) моделлю пам'яті: вказівники далеко (32-бітні), але окремі об’єкти обмежені 64 к (тому розмір_t може бути 16-бітовим).
dan04

8
"розмір найбільшого об'єкта" не є поганим формулюванням, але абсолютно правильним. Шістдесят об'єкта можуть бути значно більш обмеженими, ніж адресний простір.
gnasher729

3
"ваш компілятор може зробити припущення про це": Я сподіваюся, що компілятор знає точний діапазон значень, який size_tможе представляти! Якщо це не так, хто робить?
Марк ван Левен

4
@Marc: Я думаю, справа в тому, що компілятор міг би зробити щось із цим знанням.

8
Я просто хочу, щоб цей все більш популярний тип не вимагав включення заголовкового файлу.
користувач2023370

98

Класичний С (ранній діалект С, описаний Брайаном Керніган і Деннісом Річі в "Мові програмування на C", Prentice-Hall, 1978) не передбачав size_t. Комітет зі стандартів C запроваджено size_tдля усунення проблеми переносимості

Докладно пояснено на embedded.com (з дуже хорошим прикладом)


6
Ще одна чудова стаття, що пояснює і size_t, і ptrdiff_t: viva64.com/en/a/0050
Ігор Кахарліченко

73

Коротше кажучи, size_tце ніколи не є негативним, і це забезпечує максимальну ефективність, тому що це typedef'd - це цілий цільовий неподписаний тип, який є достатньо великим, але не надто великим - для розміру найбільшого можливого об'єкта на цільовій платформі.

Розміри ніколи не повинні бути негативними, і справді size_tце непідписаний тип. Крім того, оскільки size_tце не підписано, ви можете зберігати числа, приблизно вдвічі більші, ніж у відповідному підписаному типі, тому що ми можемо використовувати біт знака для представлення величини, як і всі інші біти в цілому цілі, що не підписується. Коли ми отримуємо ще один біт, ми множимо діапазон чисел, який ми можемо представляти, на коефіцієнт приблизно два.

Отже, ви запитаєте, чому б просто не використовувати unsigned int? Він може не мати достатньо великої кількості. У реалізації, де unsigned int32 біти, найбільша кількість вона може представляти 4294967295. Деякі процесори, такі як IP16L32, можуть копіювати об'єкти розміром більше 4294967295байтів.

Отже, запитаєте ви, чому б не використати unsigned long int? Це вимагає плати за продуктивність на деяких платформах. Стандарт C вимагає, щоб він longзаймав принаймні 32 біти. Платформа IP16L32 реалізує кожну 32-бітну довжину як пару 16-бітних слів. Практично всі 32-розрядні оператори на цих платформах вимагають двох інструкцій, якщо не більше, тому що вони працюють з 32 бітами в двох 16-бітових фрагментах. Наприклад, для переміщення по 32-бітовій довжині зазвичай потрібні дві інструкції на машині - одна для переміщення кожного 16-бітного куска.

Використання size_tдозволяє уникнути цієї плати за продуктивність. Згідно з цією фантастичною статтею , "Тип size_t- це typedef, який є псевдонімом певного цілого цілого числа без підпису, як правило, unsigned intабо unsigned long, можливо, навіть unsigned long long. Кожна реалізація Standard C повинна вибирати ціле цільове число без підпису, але не більше, ніж потрібно-- представляти розмір найбільшого можливого об'єкта на цільовій платформі. "


1
Вибачте за те, що прокоментував це через стільки часу, але мені просто довелося підтвердити найбільше число, яке може містити неподписаний int - можливо, я неправильно розумію вашу термінологію, але я подумав, що найбільше число, яке може містити неподписаний int, - це 4294967295, 65356 максимум неподписаного короткого.
Мітч

Якщо ваш непідписаний int займає 32 біти, то так, найбільше число, яке він може вмістити, становить 2 ^ 32 - 1, що становить 4294967295 (0xffffffffff). У вас є інше питання?
Роза Перроне

3
@Mitch: Найбільше значення, яке може бути представлене в консервній банці, unsigned intі коливається, залежить від однієї системи до іншої. Потрібно, щонайменше 65536 , але в деяких системах це 4294967295може бути 18446744073709551615(2 ** 64-1).
Кіт Томпсон

1
Найбільше значення, яке може містити 16-бітний неподписаний int, - 65535, а не 65536. Невелика, але важлива різниця, як 65536, така сама, як 0 у 16-бітовому неподписаному int.
Sie Raybould

1
@ gnasher729: Ви впевнені у стандарті C ++? Шукаючи деякий час, я маю враження, що вони просто зняли всі абсолютні гарантії щодо цілих діапазонів (за винятком unsigned char). Здається, стандарт ніде не містить рядка "65535" або "65536", а "+32767" зустрічається лише (1,9: 9) у примітці як можливе найбільше ціле число, яке може бути представлене в int; не дається гарантія навіть того, що INT_MAXне може бути меншим за це!
Marc van Leeuwen

51

Тип size_t - це тип, що повертається оператором sizeof. Це ціле число без підпису, здатне виражати розмір у байтах будь-якого діапазону пам'яті, підтримуваного на хост-машині. Це (як правило) пов'язано з ptrdiff_t тим, що ptrdiff_t - це ціле цільове значення, яке підписується таким чином, що sizeof (ptrdiff_t) і sizeof (size_t) рівні.

При написанні коду C ви завжди повинні використовувати size_t, коли маєте справу з діапазонами пам'яті.

З іншого боку, тип int визначається як розмір цілого (підписаного) цілого значення, який хост-машина може використовувати для найбільш ефективного виконання арифметики цілочисленних чисел. Наприклад, на багатьох старих комп'ютерах типу ПК значення sizeof (size_t) було б 4 (байт), але sizeof (int) було б 2 (байт). 16-бітна арифметика була швидшою, ніж 32-бітна арифметика, хоча процесор міг обробляти (логічний) простір пам'яті до 4 Гб.

Використовуйте тип int лише тоді, коли ви дбаєте про ефективність, оскільки його фактична точність сильно залежить як від параметрів компілятора, так і від архітектури машини. Зокрема, стандарт C визначає такі інваріанти: sizeof (char) <= sizeof (короткий) <= sizeof (int) <= sizeof (довгий), не встановлюючи інших обмежень щодо фактичного подання точності, доступної програмісту для кожного з ці примітивні типи.

Примітка. Це НЕ те саме, що в Java (що фактично визначає бітову точність для кожного з типів "char", "byte", "short", "int" і "long").


де-факто визначення int полягає в тому, що це 16 біт на 16 машинах і 32 біт на будь-яких великих розмірах. Занадто багато написано коду, який передбачає, що int має 32 біти, щоб змінити це зараз, і в результаті люди завжди повинні використовувати size_t або {, u} int {8,16,32,64} _t, якщо хочуть чогось конкретного - - в якості запобіжних заходів люди повинні завжди використовувати їх замість цілих цілих типів.
Чіткіший

3
"Це ціле число без підпису, здатне виражати розмір у байтах будь-якого діапазону пам'яті, підтримуваного на хост-машині." -> Ні. size_tЗдатний представляти розмір будь-якого окремого об'єкта (наприклад, число, масив, структура). Весь діапазон пам’яті може перевищуватиsize_t
chux - Відновіть Моніку

"При написанні коду C ви завжди повинні використовувати size_t, коли маєте справу з діапазонами пам'яті." - це означає, що кожен індекс для кожного масиву повинен бути size_t- я сподіваюся, ви цього не маєте на увазі. Здебільшого ми не маємо справи з масивами, де важливість навіть простоти адресного простору та портативності. У цих випадках ви приймете size_t. У кожному іншому випадку ви приймаєте індекси з (підписаних) цілих чисел. Тому що плутанина (що настає без попередження), що виникає через несподівану поведінку під підписом неподписаних, зустрічається частіше і гірше, ніж проблеми переносимості, які можуть виникнути в інших випадках.
johannes_lalala

23

Тип size_t повинен бути достатньо великим, щоб зберігати розмір будь-якого можливого об’єкта. Непідписаний int не повинен задовольняти цій умові.

Наприклад, у 64-бітових системах int та unsigned int можуть бути шириною 32 біти, але size_t повинен бути достатньо великим, щоб зберігати номери більше 4G


38
"об'єкт" - це мова, що використовується стандартом.
R .. GitHub СТОП ДОПОМОГАТИ

2
Я думаю, size_tце було б таким великим, лише якщо компілятор міг прийняти тип X такий, що sizeof (X) дасть значення, що перевищує 4G. Більшість компіляторів відмовляться, наприклад typedef unsigned char foo[1000000000000LL][1000000000000LL], і навіть foo[65536][65536];можуть бути законно відхилені, якби це перевищувало встановлений документально визначений ліміт.
supercat

1
@MattJoiner: Формулювання чудово. "Об'єкт" зовсім не розпливчастий, а скоріше визначений як "область зберігання".
Гонки легкості на орбіті

4

Цей уривок з посібника glibc 0.02 також може бути актуальним при дослідженні теми:

Існує потенційна проблема з типом size_t та версіями GCC до випуску 2.4. ANSI C вимагає, щоб size_t завжди був непідписаним типом. Для сумісності з файлами заголовків існуючих систем GCC визначає size_t в stddef.h' to be whatever type the system'ssys / types.h 'визначає його як. Більшість систем Unix, які визначають size_t в `sys / types.h ', визначають його як підписаний тип. Деякий код у бібліотеці залежить від того, що size_t є непідписаним типом, і він не працюватиме правильно, якщо він підписаний.

Код бібліотеки GNU C, який очікує непідписання size_t, є правильним. Визначення size_t як підписаного типу є невірним. Ми плануємо, що у версії 2.4 GCC завжди визначатиме size_t як fixincludes' script will massage the system'sнеподписаний тип, а sys / types.h ', щоб не суперечити цьому.

Тим часом ми вирішуємо цю проблему, кажучи явно GCC використовувати неподписаний тип size_t при компіляції бібліотеки GNU C. `configure 'автоматично визначить, який тип GCC використовує для size_t домовитись, щоб змінити його, якщо це необхідно.


2

Якщо мій компілятор встановлений на 32 біт, size_tце не що інше, як typedef для unsigned int. Якщо мій компілятор встановлений на 64 біт, size_tце не що інше, як typedef для unsigned long long.


1
Можна просто визначити як unsigned longдля обох випадків на деяких ОС.
StaceyGirl

-4

size_t - розмір вказівника.

Отже, в 32 бітах або загальній моделі ILP32 (ціла, довга, покажчик) модель size_t становить 32 біта. а в 64 бітах або загальна модель LP64 (довга, вказівна) size_t становить 64 біта (цілі числа все ще 32 біти).

Є й інші моделі, але це ті, які використовують g ++ (принаймні, за замовчуванням)


15
size_tне обов'язково має той же розмір, що і вказівник, хоча це зазвичай. Вказівник повинен бути в змозі вказувати на будь-яке місце в пам'яті; size_tлише має бути достатньо великим, щоб відображати розміри найбільшого одного об'єкта.
Кіт Томпсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.