Що таке неподписаний знак?


479

Для чого C / C ++ для чого unsigned charвикористовується? Чим він відрізняється від звичайного char?

Відповіді:


548

У C ++ є три різних типи символів:

  • char
  • signed char
  • unsigned char

Якщо ви використовуєте типи символів для тексту , використовуйте некваліфіковані char:

  • це тип буквених символів типу 'a'або '0'.
  • саме такий тип складається з C рядків "abcde"

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

Якщо ви використовуєте типи символів як числа , використовуйте:

  • signed char, що дає вам принаймні діапазон від -127 до 127. (Від -128 до 127 є загальним)
  • unsigned char, що дає вам принаймні діапазон від 0 до 255.

"Принаймні", тому що стандарт C ++ дає лише мінімальний діапазон значень, який повинен охоплювати кожен числовий тип. sizeof (char)повинен бути 1 (тобто один байт), але теоретично байт може становити, наприклад, 32 біти. sizeofяк і раніше буде повідомляти його розмір як1 - означає, що ви могли мати sizeof (char) == sizeof (long) == 1.


4
Щоб було зрозуміло, чи могли б у вас бути 32-бітні символи та 32-бітні цілі числа, і розмір має розмір (int)! = Sizeof (char)? Я знаю, що стандарт каже: sizeof (char) == 1, але чи відносний розмірof (int) заснований на фактичній різниці в розмірах або різниці в діапазоні?
Джозеф Гарвін

14
+1. Але в C ++ є чотири різних типи символів, wchar_t - один з них.
Eric Z

11
оскільки c ++ 11 у вас є 6 різних типів: char, підписаний char, неподписаний char, wchar_t, char16_t, char32_t.
marcinj

12
@unheilig Останнє розміщення пробілу звичайно, sizeofоскільки це не функція, а оператор. Дуже кращим стилем є опускання дужок при прийнятті розміру змінної. sizeof *pабо sizeof (int). Це дає зрозуміти швидко, якщо це стосується типу або змінної. Точно також надмірно ставити дужки після return. Це не функція.
Патрік Шлютер

3
" char: це тип буквених символів типу" 'a'або '0'"." вірно в C ++, але не в C. В C - 'a'це an int.
chux

92

Це залежить від реалізації, оскільки стандарт C НЕ визначає підписання char. Залежно від платформи, char може бути signedабо unsigned, тому вам потрібно чітко запитати signed charабо unsigned charякщо від цього залежить ваша реалізація. Просто використовуйте, charякщо ви маєте намір представляти символи з рядків, оскільки це буде відповідати тому, що ваша платформа розміщує в рядку.

Різниця між signed charі unsigned charтака, як і слід було очікувати. На більшості платформ signed charбуде 8-бітове число доповнення двох, починаючи з -128до 127, і unsigned charбуде 8-бітним цілим числом ( 0до 255). Зверніть увагу, що стандарт НЕ вимагає, щоб у charтипів було 8 біт, тільки те sizeof(char)повернення 1. Ви можете отримати кількість бітів в картці з CHAR_BITв limits.h. Сьогодні мало таких платформ, де це буде щось інше, ніж 8, однак.

Існує резюме хорошого цього питання тут .

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


2
підписані знаки мають лише мінімальний діапазон від -127 до 127, а не від -128 до 127
12431234123412341234123

3
@ 12431234123412341234123: Технічно вірно, оскільки стандарт C визначає від -127 до 127 як мінімальний діапазон. Я закликаю вас знайти платформу, яка не використовує арифметику доповнення двох. Майже на кожній сучасній платформі фактичний діапазон підписаних знаків становитиме від -128 до 127.
Тодд Гамблін

CHAR_BITстандарт повинен бути не менше 8 біт.
мартінкунев

39

Оскільки я відчуваю, що це дійсно вимагається, я просто хочу викласти деякі правила C і C ++ (у цьому плані вони однакові). По- перше, все біти від unsigned charучасті у визначенні вартості , якщо якийсь - або об'єкт без знака напівкоксу. По-друге, unsigned charпрямо вказано без підпису.

Тепер у мене була дискусія з кимось про те, що відбувається при перетворенні значення -1типу int в unsigned char. Він відмовився від ідеї про те, що в результаті unsigned charцього всі його біти встановлені на 1, тому що він переживав за представлення знаків. Але йому не потрібно. З цього правила одразу випливає, що конверсія виконує те, що призначено:

Якщо новий тип не підписаний, значення перетворюється шляхом багаторазового додавання або віднімання на одне більше, ніж максимальне значення, яке можна представити в новому типі, поки значення не буде в діапазоні нового типу. ( 6.3.1.3p2у проекті C99)

Це математичний опис. C ++ описує це з точки зору модульного обчислення, яке поступається тим самим правилам. У будь-якому випадку, що не гарантується, це те, що всі біти в цілому числі -1є одним до перетворення. Отже, що ми маємо, щоб ми могли стверджувати, що отриманий результат unsigned charмає всі свої CHAR_BITбіти перетворені на 1?

  1. Усі біти беруть участь у визначенні його значення - тобто жодних біт для прокладки в об'єкті не відбувається.
  2. Додавання лише одного разу UCHAR_MAX+1до -1дасть значення в діапазоні, а самеUCHAR_MAX

Насправді цього достатньо! Отже, коли ви хочете мати unsigned charвсі свої біти один, ви робите

unsigned char c = (unsigned char)-1;

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


2
Чому б просто не використовувати UCHAR_MAX?
Nicolás

1
Бо (unsigned type)-1це якась ідіома. ~0ні.
Патрік Шлютер

1
якщо у мене є щось подібне int x = 1234і char *y = &x. Бінарне представлення 1234 є 00000000 00000000 00000100 11010010. Моя машина мало ендіанська, тому вона перевертає її і зберігання в пам'яті 11010010 00000100 00000000 00000000LSB приходить першим. Тепер основна частина. якщо я використовую printf("%d" , *p). printfбуде читати перший байт 11010010тільки вихід, -46але 11010010це 210так, чому він друкує -46. Я дуже розгублений, я здогадуюсь, що якась знакова цільна акція щось робить, але я не знаю.
Сурай Джайн

27

Що стосується, наприклад, використання неподписаних знаків :

unsigned charчасто використовується в комп'ютерній графіці, яка дуже часто (хоча і не завжди) призначає кожен байт кожному кольоровому компоненту. Загальноприйнято бачити колір RGB (або RGBA), представлений у вигляді 24 (або 32) біт, кожен з них unsigned char. Оскільки unsigned charзначення потрапляють у діапазон [0,255], значення зазвичай інтерпретуються як:

  • 0 означає повний брак даної колірної складової.
  • 255, що означає 100% певного кольорового пігменту.

Таким чином, ви отримаєте червоний RGB як (255,0,0) -> (100% червоний, 0% зелений, 0% синій).

Чому б не використовувати a signed char? Арифметика та зміщення бітів стає проблематичним. Як уже було пояснено, signed charдіапазон 's суттєво зміщується на -128. Дуже простий і наївний (здебільшого невикористаний) метод перетворення RGB в масштаб сірого - це середня оцінка всіх трьох кольорових компонентів, але це виникає у проблемах, коли значення кольорових компонентів від'ємні. Червоний (255, 0, 0) в середньому дорівнює (85, 85, 85) при використанні unsigned charарифметики. Однак якби значення були signed chars (127, -128, -128), ми б закінчилися (-99, -99, -99), що було б (29, 29, 29) у нашому unsigned charпросторі, що невірно .


13

Якщо ви хочете використовувати символ як мале ціле число, найбезпечніший спосіб це зробити з типами int8_tта uint8_t.


2
Не дуже хороша ідея: int8_tа НЕ uint8_tє обов'язковими і не визначені на архітектурі , де розмір байт не рівно 8 біт. З іншого боку , signed charі unsigned charзавжди доступні і гарантовано трюму не менше 8 біт. Це може бути звичайний спосіб, але не найбезпечніший .
chqrlie

2
Це коментар, він не відповідає на питання.
Лундін

@chqrlie Отже, ви маєте на увазі, справді найбезпечнішим способом представити мале ціле число, якщо ви хочете зберегти пам'ять, - це зберігати signed charі unsigned char? Або ви б рекомендували кращу "безпечнішу" альтернативу саме в цьому випадку? Наприклад, дотримуватися "справжніх" цілих типів signed intі unsigned intзамість цього чомусь?
RobertS підтримує Моніку Селліо

@ RobertS-ReinstateMonica: Використання signed charі unsigned charпереноситься для всіх відповідних реалізацій і заощадить місце для зберігання, але може призвести до збільшення розміру коду. У деяких випадках можна заощадити більше місця для зберігання, зберігаючи невеликі значення в бітових полях або одиночних бітах регулярних цілих типів. На це питання немає абсолютної відповіді, доречність такого підходу залежить від конкретного випадку. І ця відповідь ніяк не стосується питання.
chqrlie

10

unsigned charприймає лише позитивні значення .... як 0 до 255

де як

signed charприймає як позитивні, так і негативні значення .... як -128 до +127


9

charі unsigned charне гарантується, що вони будуть 8-бітовими типами на всіх платформах - вони гарантовано будуть 8-бітовими або більшими. Деякі платформи мають 9-бітні, 32-бітні або 64-бітні байти . Однак найпоширеніші сьогодні платформи (Windows, Mac, Linux x86 тощо) мають 8-бітні байти.


8

signed charмає діапазон від -128 до 127; unsigned charмає діапазон від 0 до 255.

char буде еквівалентним або підписаним char, або неподписаним char, залежно від компілятора, але є виразним типом.

Якщо ви використовуєте рядки в стилі C, просто використовуйте char. Якщо вам потрібно використовувати символи для арифметики (досить рідко), вкажіть підписані або непідписані явно для переносу.


8

Ан unsigned char- значення без байту (0 до 255). Ви можете думати про charте, щоб бути "персонажем", але це дійсно числове значення. Звичайний charпідписаний, тому у вас є 128 значень, і ці значення відображаються на символи, використовуючи кодування ASCII. Але в будь-якому випадку те, що ви зберігаєте в пам'яті, є значенням байта.


7

З точки зору прямих значень використовується звичайний знак, коли значення, як відомо, знаходяться між, CHAR_MINі CHAR_MAXтоді як неподписаний знак забезпечує подвійний діапазон на позитивному кінці. Наприклад, якщо CHAR_BITце 8, діапазон регулярних charгарантовано лише [0, 127] (оскільки він може бути підписаний або без підпису), тоді як unsigned charбуде [0, 255] і signed charбуде [-127, 127].

Що стосується того, для чого він використовується, то стандарти дозволяють об'єктам POD (звичайні старі дані) безпосередньо перетворюватися на масив неподписаних знаків. Це дозволяє вивчити представлення та бітові шаблони об’єкта. Таких же гарантій безпечного виду покарання не існує для char або підписаних char.


Власне, найчастіше це буде [-128, 128].
RastaJedi

Стандарти лише формально визначити уявлення об'єкта у вигляді послідовності з unsigned char, а НЕ масиву конкретно, і будь-який «перетворення» тільки формально визначаються копіювання від об'єкта до реального, оголошеному масиву з unsigned char& потім перевірок останніх. Незрозуміло, чи АБО можна безпосередньо переосмислити як такий масив, з припущеннями для арифметики вказівника це спричинило б, тобто чи "послідовність" =="масиву" в цьому використанні. Основний випуск № 1701 відкритий з надією на уточнення цього питання. На щастя, оскільки ця двозначність насправді непокоїть мене останнім часом.
підкреслюй_

1
@RastaJedi Ні, це не буде. Це не може. Діапазон від -128 ... + 128 фізично неможливо представити за допомогою 8 біт. Ця ширина підтримує лише 2 ^ 8 == 256 дискретних значень, але -128 ... + 128 = 2 * 128 + 1 при 0 = 257. Представлення значень величини дозволяє -127 ... + 127, але має 2 (біполярне) нулі. Представлення двох доповнень підтримує єдиний нуль, але складає діапазон, маючи ще одне значення на негативній стороні; це дозволяє -128 ... + 127. (І так для обох при більшій ширині бітів.)
підкреслюю

Повторюючи мій другий коментар, доцільно припустити, що ми можемо взяти покажчик на 1 unsigned char-у АБО, а потім продовжувати використовувати ++ptrзвідти, щоб прочитати кожен байт цього тексту ... Але AFAICT, це не визначено як дозволене, тому ми залишилося зробити висновок, що це "ймовірно нормально" з безлічі інших уривків (і багато в чому, просто існування memcpy) у Стандарті, схожих на головоломку. Що не ідеально. Ну, може, формулювання з часом покращиться. Ось питання про CWG, про який я згадав, але не вистачало місця для посилання - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d

@underscore_d вибачте, це був помилковий помилок. [-128, 127] - це те, що я мав намір ввести: с. Так, я знаю про подвійні нулі ("позитивний" і "негативний" нуль) зі знаком / величиною. Я, мабуть, втомився: с.
RastaJedi

5

unsigned charє серцем усіх хитрощів. Практично ВСЕ компілятор для ВСІХ платформи а unsigned char- просто байт і непідписане ціле число (зазвичай) 8 біт, яке можна розглядати як мале ціле число або пакет біт.

У наркоманії, як сказав хтось інший, стандарт не визначає ознаки знаку. тому у вас є 3 різних charтипів: char, signed char, unsigned char.


1
Біт-хитрість, ака-бит-подвійність або хакерство, справді відомо, що викликає звикання ;-)
chqrlie

3
Саме 0 викликають проблеми. Щоб уникнути звикання від щебетання, тримайтеся подалі від нудних шматочків.
DragonLord

5

Якщо вам подобається , використовуючи різні типи довжини конкретного і знаковості, ви , ймовірно , краще з uint8_t, int8_t, uint16_tі т.д. , просто тому , що вони роблять саме те , що вони говорять.


4

Деякі googling знайшли це , де люди мали дискусію з цього приводу.

Непідписаний значок - це в основному один байт. Отже, ви б використали це, якщо вам потрібен один байт даних (наприклад, можливо, ви хочете використовувати його для встановлення і вимкнення прапорів для передачі функції, як це часто робиться в API Windows).


4

Непідписаний знак char використовує біт, який зарезервований для знаку звичайного знака, як інше число. Це змінює діапазон на [0 - 255] на відміну від [-128 - 127].

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


4

unsigned charприймає лише позитивні значення: 0 до 255, тоді як signed charприймає позитивні та негативні значення: від -128 до +127.


3

процитував книгу "c програмою для прання":

Класифікатор signedабо unsignedможуть бути застосовані до напівкоксу або будь-яке ціле число. непідписані числа завжди позитивні або нульові і підкоряються законам арифметичної модулі 2 ^ n, де n - кількість бітів типу. Так, наприклад, якщо символи 8 біт, непідписані змінні символи мають значення від 0 до 255, тоді як підписані символи мають значення від -128 до 127 (у машині з двома доповненнями). -залежні, але символи для друку завжди позитивні.


2

signed charі unsigned charобидва представляють 1 байт, але вони мають різний діапазон.

   Type        |      range
-------------------------------
signed char    |  -128 to +127
unsigned char  |     0 to 255

У signed charразі ми вважаємо char letter = 'A', «А» є виконавчі 65 в ASCII/Unicode, якщо 65 може бути збережена, -65 також може бути збережений. Немає негативних бінарних значень уASCII/Unicode і не потрібно турбуватися про негативні значення.

Приклад

#include <stdio.h>

int main()
{
    signed char char1 = 255;
    signed char char2 = -128;
    unsigned char char3 = 255;
    unsigned char char4 = -128;

    printf("Signed char(255) : %d\n",char1);
    printf("Unsigned char(255) : %d\n",char3);

    printf("\nSigned char(-128) : %d\n",char2);
    printf("Unsigned char(-128) : %d\n",char4);

    return 0;
}

Вихід -:

Signed char(255) : -1
Unsigned char(255) : 255

Signed char(-128) : -128
Unsigned char(-128) : 128
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.