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.