uint8_t vs неподписаний знак


231

Яка перевага використання uint8_tнад unsigned charC?

Я знаю, що майже в кожній системі uint8_tє просто typedef для unsigned char, так навіщо це використовувати?

Відповіді:


225

Це документує ваш намір - ви будете зберігати невеликі числа, а не символ.

Крім того, це виглядає приємніше, якщо ви використовуєте інші typedefs, такі як uint16_tабо int32_t.


1
У первісному питанні було не ясно, ми говорили про стандартний тип чи ні. Я впевнений, що протягом багатьох років цієї конвенції про іменування було багато варіантів.
Марк Викуп

8
Явно використовуючи unsigned charабо signed charдокументуючи наміри теж, оскільки без реклами char- це те, що показує, що ви працюєте з персонажами.
caf

9
Я думав, що неприхований unsignedбув unsigned intза визначенням?
Марк Викуп

5
@endolith, використання uint8_t для рядка не обов'язково помиляється, але це, безумовно, дивно.
Марк Викуп

5
@endolith, я думаю, я можу зробити справу для uint8_t з текстом UTF8. Насправді, charсхоже, йдеться про символ, тоді як у контексті рядка UTF8 це може бути лише один байт багатобайтового символу. Використання uint8_t може дати зрозуміти, що не слід очікувати символу в кожній позиції - іншими словами, кожен елемент рядка / масиву є довільним цілим числом, про яке не слід робити жодних семантичних припущень. Звичайно, це знають усі програмісти на C, але це може підштовхнути новачків до правильних питань.
tne

70

Деякі системи, щоб бути педантичними, можуть не мати 8-бітового типу. Згідно Вікіпедії :

Потрібна реалізація для визначення цілочисельних типів точної ширини для N = 8, 16, 32 або 64, якщо і лише тоді, коли вона має будь-який тип, який відповідає вимогам. Не потрібно їх визначати для будь-яких інших N, навіть якщо вони підтримують відповідні типи.

Тому uint8_tне гарантовано існування, хоча це буде для всіх платформ, де 8 біт = 1 байт. Деякі вбудовані платформи можуть бути різними, але це стає дуже рідко. Деякі системи можуть визначати charтипи, що мають 16 біт, і в цьому випадку, ймовірно, не буде 8-бітового типу.

Окрім цього (другорядного) питання, на мою думку , відповідь @Mark Ransom - найкраща. Використовуйте той, який найбільш чітко показує, для чого ви використовуєте дані.

Крім того, я припускаю, що ви мали на увазі uint8_t(стандартний typedef від C99, наданий у stdint.hзаголовку), а не uint_8(не є частиною будь-якого стандарту).


3
@caf, з великої цікавості - чи можете ви посилання на опис деяких? Я знаю, що вони існують, тому що хтось згадав про нього (і пов’язаний з документами для розробників для цього) в comp.lang.c ++. Модерував дискусію про те, чи є гарантії типу C / C ++ занадто слабкими, але я вже не можу знайти цю тему, і це завжди зручно для посилання на те, що в будь-яких подібних дискусіях :)
Павло Мінаєв

3
"Деякі системи можуть визначати типи знаків як 16 біт. У цьому випадку, ймовірно, не буде 8-бітового типу будь-якого типу." - і незважаючи на деякі невірні заперечення від мене, Павло продемонстрував у своїй відповіді, що якщо char - 16 біт, то навіть якщо компілятор надає 8-бітовий тип, він не повинен його викликати uint8_t(або вводити до цього). Це тому, що тип 8 біт мав би невикористані біти у представленні сховища, яких uint8_tне повинно бути.
Стів Джессоп

3
Архітектура SHARC має 32-бітні слова. Докладніше див. En.wikipedia.org/wiki/… .
BCran

2
А ЦСП D5 у T5 (які були в OMAP1 та OMAP2) мають 16 біт. Я думаю, що для OMAP3 вони перейшли до серії C6000, з 8-бітовою графікою.
Стів Джессоп

4
Робота в N3242 - "Робочий проект, стандарт програми для мови програмування C ++", розділ 18.4.1 конспекту "cstdint>" - typedef unsigned integer type uint8_t; // optional Отже, по суті, бібліотека, що відповідає стандарту C ++, взагалі не потрібна для визначення uint8_t (див. Коментар // необов'язково )
нічні маршрути

43

Вся справа в тому, щоб написати незалежний від реалізації код. unsigned charне гарантується, що це 8-бітний тип. uint8_tє (якщо є).


4
... якщо вона існує в системі, але це буде дуже рідко. +1
Кріс Луц

2
добре, якщо у вас дійсно виникли проблеми з тим, що ваш код не компілюється в системі, оскільки uint8_t не існувало, ви можете використовувати find і sed, щоб автоматично змінити всі випадки uint8_t на неподписані char або щось більш корисне для вас.
баз

2
@bazz - ні, якщо ви вважаєте, що це 8-бітний тип, який ви не можете - наприклад, розпаковувати дані, упаковані в бічний спосіб шляхом віддаленої системи. Неявне припущення полягає в тому, що причина uint8_t не існує в процесорі, де значення char перевищує 8 біт.
Кріс Страттон

кинути твердження твердження (sizeof (неподписаний знак) == 8);
базз

3
@bazz неправильне твердження Боюся. sizeof(unsigned char)повернеться 1за 1 байт. але якщо системні char та int мають однаковий розмір, наприклад, 16-бітові, то sizeof(int)також повернуться1
Toby

7

Як ви сказали, " майже кожна система".

charце, мабуть, одна з менших шансів змінити, але як тільки ви почнете використовувати uint16_tта дружити, uint8_tкраще використовуйте суміші, і навіть може бути частиною стандарту кодування.


7

На мій досвід, є два місця, де ми хочемо використовувати uint8_t, щоб означати 8 біт (і uint16_t тощо), і де ми можемо мати поля менше 8 біт. В обох місцях є місце, де важливий простір, і нам часто потрібно дивитися на неочищений дамп даних при налагодженні, і потрібно мати можливість швидко визначити, що це таке.

Перший - у протоколах РФ, особливо у вузькосмугових системах. У цьому середовищі нам може знадобитися запакувати якомога більше інформації в одне повідомлення. Друга - у флеш-пам’яті, де у нас може бути дуже обмежений простір (наприклад, у вбудованих системах). В обох випадках ми можемо використовувати структуру упакованих даних, в якій компілятор подбає про упаковку та розпакування для нас:

#pragma pack(1)
typedef struct {
  uint8_t    flag1:1;
  uint8_t    flag2:1;
  padding1   reserved:6;  /* not necessary but makes this struct more readable */
  uint32_t   sequence_no;
  uint8_t    data[8];
  uint32_t   crc32;
} s_mypacket __attribute__((packed));
#pragma pack()

Який метод ви використовуєте, залежить від вашого компілятора. Також вам може знадобитися підтримка декількох різних компіляторів з однаковими файлами заголовків. Це відбувається у вбудованих системах, де пристрої та сервери можуть бути абсолютно різними - наприклад, у вас може бути пристрій ARM, який спілкується з сервером x86 Linux.

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

Деякі люди також будуть турбуватися про продуктивність і стверджують, що використання цих упакованих структур уповільнить вашу систему. Це правда, що поза кадром компілятор додає код для доступу до неприєднаних членів даних. Ви можете це бачити, дивлячись на код складання у вашому IDE.

Але оскільки упаковані структури є найбільш корисними для зв'язку та зберігання даних, то дані можуть бути вилучені в непакетоване представлення під час роботи з ним у пам'яті. Зазвичай нам не потрібно працювати з усім пакетом даних у пам'яті.

Ось відповідна дискусія:

pragma pack (1) ні __attribute__ ((вирівняний (1))) працює

Чи небезпечний __attribute __attribute __ ((упакований)) gcc / #pragma?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html


6

Тут мало. З точки зору портативності, вона charне може бути меншою ніж 8 біт, і нічого не може бути меншим char, тому, якщо дана реалізація C має непідписаний 8-бітний цілочисельний тип, це буде char. Крім того, він може не мати його взагалі, і в цей момент будь-які typedefтрюки суперечать.

Це можна використовувати для кращого документування вашого коду в сенсі, що зрозуміло, що вам потрібні 8-бітні байти та нічого іншого. Але на практиці це вже розумне очікування практично в будь-якому місці (є платформи DSP, на яких це неправда, але шанси вашого коду там невисокі, і ви можете так само помилитися, використовуючи статичне затвердження у верхній частині вашої програми на така платформа).


7
@Skizz - Ні, стандарт вимагає, unsigned charщоб можна було утримувати значення між 0 і 255. Якщо ви можете це зробити в 4 біти, моя шапка знімається до вас.
Кріс Лутц

1
"це було б трохи громіздкіше" - громіздке в тому сенсі, що вам доведеться ходити (плавати, ловити літак тощо) аж до місця, де був автор-компілятор, ляпайте їх по потилиці , і додати їх uint8_tдо реалізації. Цікаво, чи компілятори для DSP з 16-бітовими символами зазвичай реалізують uint8_t, чи ні?
Стів Джессоп

6
До речі, по-другому, це, мабуть, найпростіший спосіб сказати «мені дуже потрібно 8 біт» - #include <stdint.h>і використовувати uint8_t. Якщо платформа має її, вона передасть її вам. Якщо на платформі її немає, ваша програма не збиратиметься, а причина буде зрозумілою та зрозумілою.
Павло Мінаєв

2
Ще немає сигари, вибачте: "Для цілих цілей без підпису, окрім непідписаних знаків, біти представлення об'єкта поділяються на дві групи: біти значення та біти заполнення ... Якщо є N бітів значення, кожен біт повинен представляти різні потужність 2 між 1 і 2 ^ (N-1), так що об'єкти цього типу повинні бути здатні представляти значення від 0 до 2 ^ (N-1), використовуючи чисте двійкове подання ... Ім'я typedef intN_t позначає a підписаний цілочисельний тип із шириною N, відсутністю бітів для замітки та поданням доповнення двох ".
Павло Мінаєв

1
Якщо вам просто потрібна арифметична модуль, непідписаний бітфілд буде добре (якщо незручно). Коли вам потрібен, скажімо, масив октетів без прокладки, це коли ви SOL. Мораль історії - не кодувати DSP, а дотримуватися належних, чесних богам 8-бітових архітектурних чарів :)
Павло Мінаєв

4

Це дуже важливо, наприклад, коли ви пишете мережевий аналізатор. заголовки пакетів визначаються специфікацією протоколу, а не тим, як працює компілятор C певної платформи.


Ще коли я запитав це, я визначив простий протокол передачі повідомлення про серійні.
Ліндон Уайт

2

Практично в кожній системі, з якою я зустрічався, не підписаний знак uint8_t ==, але це не гарантується стандартом C. Якщо ви намагаєтеся написати портативний код і має значення саме того, який розмір пам'яті, використовуйте uint8_t. В іншому випадку використовуйте неподписані знаки.


3
uint8_t завжди відповідає діапазону та розміру unsigned charта прокладці (немає), коли unsigned char 8-бітний. Коли unsigned charне є 8-бітовим, uint8_tне існує.
chux

@chux, Чи маєте ви посилання на точне місце в стандарті, де це сказано? Якщо unsigned charце 8-біт, буде uint8_tгарантовано буде typedefїх , а не typedefз розширеного цілого числа без знака типу ?
hsivonen

@hsivonen "точне місце в стандарті, де це сказано?" -> Ні - ще дивіться на 7.20.1.1. Він легко виводиться, як unsigned char/signed char/charі найменший тип - не менше 8 біт. unsigned charне має прокладки. Щоб uint8_tце було, воно повинно бути 8-бітним, без прокладки, щоб існувати через цілий цілий тип, що відповідає: відповідає мінімальним вимогам unsigned char. Щодо "... гарантовано, що буде typedef ..." виглядає як хороше запитання для публікації.
chux
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.