Apa itu char yang tidak ditandatangani?


479

Dalam C / C ++, untuk apa sebuah unsigned chardigunakan? Apa bedanya dengan yang biasa char?

Jawaban:


548

Dalam C ++, ada tiga yang berbeda jenis karakter:

  • char
  • signed char
  • unsigned char

Jika Anda menggunakan jenis karakter untuk teks , gunakan yang tidak memenuhi syarat char:

  • itu adalah jenis karakter literal suka 'a'atau '0'.
  • itu adalah tipe yang membentuk string C seperti "abcde"

Itu juga berfungsi sebagai nilai angka, tetapi tidak ditentukan apakah nilai itu diperlakukan sebagai ditandatangani atau tidak ditandatangani. Waspadalah perbandingan karakter melalui ketidaksetaraan - meskipun jika Anda membatasi diri Anda pada ASCII (0-127) Anda hampir aman.

Jika Anda menggunakan tipe karakter sebagai angka , gunakan:

  • signed char, yang memberi Anda setidaknya kisaran -127 hingga 127. (-128 hingga 127 adalah umum)
  • unsigned char, yang memberi Anda setidaknya rentang 0 hingga 255.

"Setidaknya", karena standar C ++ hanya memberikan kisaran nilai minimum yang harus dicakup oleh setiap tipe numerik. sizeof (char)diperlukan 1 (yaitu satu byte), tetapi satu byte secara teori bisa jadi misalnya 32 bit. sizeofmasih akan melaporkan ukurannya sebagai1 - artinya Anda bisa memilikinya sizeof (char) == sizeof (long) == 1.


4
Untuk menjadi jelas, dapatkah Anda memiliki karakter 32-bit, dan bilangan bulat 32-bit, dan memiliki sizeof (int)! = Sizeof (char)? Saya tahu standar mengatakan sizeof (char) == 1, tetapi apakah sizeof relatif (int) didasarkan pada perbedaan aktual dalam ukuran atau perbedaan dalam rentang?
Joseph Garvin

14
+1. Tapi ada empat tipe karakter berbeda di C ++, wchar_t adalah salah satunya.
Eric Z

11
sejak c ++ 11 Anda memiliki 6 jenis berbeda: char, char yang ditandatangani, char yang tidak ditandatangani, wchar_t, char16_t, char32_t.
marcinj

12
@unheilig Sudah umum untuk menempatkan spasi setelah sizeofkarena itu bukan fungsi tetapi operator. Ini bahkan gaya yang lebih baik untuk menghilangkan tanda kurung ketika mengambil ukuran variabel. sizeof *patau sizeof (int). Ini membuatnya jelas dengan cepat jika itu berlaku untuk tipe atau variabel. Demikian juga, terlalu berlebihan untuk menempatkan tanda kurung setelah return. Itu bukan fungsi.
Patrick Schlüter

3
" char: ini adalah tipe karakter literal suka 'a'atau '0'." benar dalam C ++ tetapi tidak C. Dalam C, 'a'adalah int.
chux

92

Ini tergantung pada implementasi, karena standar C TIDAK mendefinisikan ke-ditandatangani-an dari char. Bergantung pada platform, char mungkin signedatau unsigned, jadi Anda perlu secara eksplisit meminta signed charatau unsigned charapakah implementasi Anda bergantung padanya. Cukup gunakan charjika Anda bermaksud untuk mewakili karakter dari string, karena ini akan cocok dengan apa yang platform Anda masukkan ke dalam string.

Perbedaan antara signed chardan unsigned charseperti yang Anda harapkan. Pada kebanyakan platform, signed charakan menjadi bilangan pelengkap 8-bit dua mulai dari -128ke 127, dan unsigned charakan menjadi integer 8-bit tanpa tanda ( 0hingga 255). Perhatikan standar TIDAK mengharuskan charjenis memiliki 8 bit, hanya itu sizeof(char)kembali 1. Anda bisa mendapatkan di jumlah bit dalam char dengan CHAR_BITdi limits.h. Ada beberapa jika ada platform hari ini di mana ini akan menjadi sesuatu selain 8.

Ada ringkasan yang bagus dari masalah ini di sini .

Seperti yang telah disebutkan orang lain sejak saya memposting ini, Anda lebih baik menggunakan int8_tdan uint8_tjika Anda benar-benar ingin mewakili bilangan bulat kecil.


2
char yang ditandatangani hanya memiliki kisaran minimum -127 hingga 127, tidak dari -128 hingga 127
12431234123412341234123

3
@ 12431234123412341234123: Secara teknis benar, dalam hal itu standar C mendefinisikan -127 hingga 127 sebagai kisaran minimum. Saya menantang Anda untuk menemukan platform yang tidak menggunakan aritmatika komplemen dua. Di hampir setiap platform modern, kisaran karakter yang ditandatangani sebenarnya adalah -128 hingga 127.
Todd Gamblin

CHAR_BITdiperlukan setidaknya 8 bit oleh standar.
martinkunev

39

Karena saya merasa itu benar-benar diperlukan, saya hanya ingin menyatakan beberapa aturan C dan C ++ (mereka sama dalam hal ini). Pertama, semua bit dari unsigned charberpartisipasi dalam menentukan nilai jika benda unsigned char. Kedua, unsigned charsecara eksplisit dinyatakan tidak ditandatangani.

Sekarang, saya berdiskusi dengan seseorang tentang apa yang terjadi ketika Anda mengonversi nilai -1tipe int ke unsigned char. Dia menolak gagasan bahwa hasilnya unsigned charmemiliki semua bit diatur ke 1, karena dia khawatir tentang representasi tanda. Tetapi dia tidak harus melakukannya. Segera mengikuti aturan ini bahwa konversi melakukan apa yang dimaksudkan:

Jika tipe baru tidak ditandatangani, nilainya dikonversi dengan berulang kali menambahkan atau mengurangi satu lebih dari nilai maksimum yang dapat direpresentasikan dalam tipe baru hingga nilainya berada dalam kisaran tipe baru. ( 6.3.1.3p2dalam konsep C99)

Itu deskripsi matematis. C ++ menjelaskannya dalam hal modulo calculus, yang menghasilkan aturan yang sama. Bagaimanapun, apa yang tidak dijamin adalah bahwa semua bit dalam integer -1adalah satu sebelum konversi. Jadi, apa yang kita miliki sehingga kita dapat mengklaim bahwa hasilnya unsigned charmemiliki semua CHAR_BITbitnya berubah menjadi 1?

  1. Semua bit berpartisipasi dalam menentukan nilainya - yaitu, tidak ada bit padding yang terjadi pada objek.
  2. Menambahkan hanya satu kali UCHAR_MAX+1ke -1akan menghasilkan nilai dalam rentang, yaituUCHAR_MAX

Sudah cukup, sebenarnya! Jadi, kapan pun Anda ingin unsigned charmemiliki semua bitnya, Anda memilikinya

unsigned char c = (unsigned char)-1;

Ini juga mengikuti bahwa konversi tidak hanya memotong bit urutan yang lebih tinggi. Acara yang beruntung untuk komplemen dua adalah bahwa itu hanya pemotongan di sana, tetapi hal yang sama tidak selalu berlaku untuk representasi tanda lainnya.


2
Kenapa tidak pakai saja UCHAR_MAX?
Nicolás

1
Karena (unsigned type)-1semacam idiom. ~0bukan.
Patrick Schlüter

1
jika saya punya sesuatu seperti ini int x = 1234dan char *y = &x. Representasi biner dari 1234 is 00000000 00000000 00000100 11010010. Mesin saya sedikit endian sehingga membalik dan menyimpannya dalam memori 11010010 00000100 00000000 00000000LSB yang lebih dulu. Sekarang Bagian Utama. jika saya gunakan printf("%d" , *p). printfakan membaca byte pertama 11010010hanya output -46tetapi 11010010adalah 210jadi mengapa mencetak -46. Saya benar-benar bingung, saya kira beberapa char to integer promotion melakukan sesuatu tetapi saya tidak tahu.
Suraj Jain

27

Sebagai contoh, penggunaan char yang tidak ditandatangani :

unsigned charsering digunakan dalam grafik komputer, yang sangat sering (meskipun tidak selalu) menetapkan satu byte untuk setiap komponen warna. Adalah umum untuk melihat warna RGB (atau RGBA) direpresentasikan sebagai 24 (atau 32) bit, masing-masing unsigned char. Karena unsigned charnilai berada dalam kisaran [0,255], nilai biasanya ditafsirkan sebagai:

  • 0 artinya tidak ada komponen warna yang diberikan.
  • 255 artinya 100% pigmen warna yang diberikan.

Jadi Anda akan berakhir dengan RGB merah sebagai (255,0,0) -> (100% merah, 0% hijau, 0% biru).

Mengapa tidak menggunakan a signed char? Aritmatika dan pergeseran bit menjadi bermasalah. Seperti yang sudah dijelaskan, signed charkisaran a pada dasarnya digeser oleh -128. Metode yang sangat sederhana dan naif (kebanyakan tidak digunakan) untuk mengkonversi RGB ke grayscale adalah dengan rata-rata ketiga komponen warna, tetapi ini mengalami masalah ketika nilai-nilai komponen warna negatif. Merah (255, 0, 0) rata-rata hingga (85, 85, 85) saat menggunakan unsigned chararitmatika. Namun, jika nilainya signed chars (127, -128, -128), kita akan berakhir dengan (-99, -99, -99), yang akan menjadi (29, 29, 29) di unsigned charruang kita , yang tidak benar .


13

Jika Anda ingin menggunakan karakter sebagai integer kecil, cara paling aman untuk melakukannya adalah dengan int8_tdan uint8_ttipe.


2
Bukan ide yang baik: int8_tdan uint8_tbersifat opsional dan tidak didefinisikan pada arsitektur di mana ukuran byte tidak tepat 8 bit. Sebaliknya, signed chardan unsigned charselalu tersedia dan dijamin untuk menampung setidaknya 8 bit. Ini mungkin cara yang umum tetapi bukan yang paling aman .
chqrlie

2
Ini komentar, tidak menjawab pertanyaan.
Lundin

@ chqrlie Jadi maksud Anda, cara teraman nyata untuk mewakili integer kecil, jika Anda ingin menghemat memori, adalah tetap menggunakan signed chardan unsigned char? Atau Anda akan merekomendasikan alternatif "aman" yang lebih baik dalam kasus tertentu? Misalnya untuk tetap dengan tipe integer "nyata" signed intdan unsigned intbukannya karena alasan tertentu?
RobertS mendukung Monica Cellio

@ RobertS-ReinstateMonica: Menggunakan signed chardanunsigned char portabel untuk semua implementasi yang sesuai dan akan menghemat ruang penyimpanan tetapi dapat menyebabkan peningkatan ukuran kode. Dalam beberapa kasus, seseorang akan menghemat lebih banyak ruang penyimpanan dengan menyimpan nilai-nilai kecil dalam bitfields atau bit tunggal dari tipe integer biasa. Tidak ada jawaban mutlak untuk pertanyaan ini, ketepatan pendekatan ini tergantung pada kasus spesifik yang dihadapi. Dan jawaban ini toh tidak menjawab pertanyaan itu.
chqrlie

10

unsigned charhanya mengambil nilai positif .... seperti 0 hingga 255

dimana sebagai

signed char mengambil nilai positif dan negatif .... seperti -128 hingga +127


9

chardan unsigned chartidak dijamin menjadi tipe 8-bit di semua platform — mereka dijamin 8-bit atau lebih besar. Beberapa platform memiliki byte 9-bit, 32-bit, atau 64-bit . Namun, platform yang paling umum saat ini (Windows, Mac, Linux x86, dll.) Memiliki byte 8-bit.


8

signed charmemiliki rentang -128 hingga 127; unsigned charmemiliki rentang 0 hingga 255.

char akan sama dengan char yang ditandatangani atau char yang tidak ditandatangani, tergantung pada kompiler, tetapi merupakan tipe yang berbeda.

Jika Anda menggunakan string gaya-C, gunakan saja char. Jika Anda perlu menggunakan karakter untuk aritmatika (sangat jarang), tentukan ditandatangani atau tidak ditandatangani secara eksplisit untuk portabilitas.


8

An unsigned charadalah nilai byte yang tidak ditandatangani (0 hingga 255). Anda mungkin berpikir untuk charmenjadi "karakter" tetapi itu benar-benar nilai numerik. Reguler charditandatangani, sehingga Anda memiliki 128 nilai, dan nilai-nilai ini dipetakan ke karakter menggunakan pengkodean ASCII. Namun dalam kedua kasus tersebut, apa yang Anda simpan dalam memori adalah nilai byte.


7

Dalam hal nilai langsung, char biasa digunakan ketika nilai diketahui berada di antara CHAR_MINdan CHAR_MAXsementara char yang tidak ditandatangani menyediakan dua kali lipat rentang di ujung positif. Misalnya, jika CHAR_BIT8, kisaran reguler charhanya dijamin [0, 127] (karena dapat ditandatangani atau tidak ditandatangani) sementara unsigned charakan [0, 255] dan signed charakan [-127, 127].

Dalam hal apa yang digunakan untuk itu, standar memungkinkan objek POD (data lama polos) untuk secara langsung dikonversi ke array char unsigned. Ini memungkinkan Anda untuk memeriksa representasi dan pola bit objek. Jaminan yang sama untuk hukuman jenis aman tidak ada untuk char atau char yang ditandatangani.


Sebenarnya, yang paling sering adalah [-128, 128].
RastaJedi

Standar hanya secara formal mendefinisikan representasi objek sebagai urutan dari unsigned char, bukan array yang khusus, & setiap "konversi" hanya secara formal didefinisikan oleh menyalin dari objek untuk nyata, menyatakan array yang dari unsigned char& kemudian memeriksa yang terakhir. Tidak jelas apakah OR dapat secara langsung ditafsirkan ulang sebagai array seperti itu, dengan kelonggaran untuk aritmatika pointer yang diperlukan, yaitu apakah "urutan" =="array" dalam penggunaan ini. Ada Isu Inti # 1701 dibuka dengan harapan mendapatkan klarifikasi ini. Syukurlah, karena ambiguitas ini benar-benar mengganggu saya baru-baru ini.
underscore_d

1
@RastaJedi Tidak, tidak akan. Tidak bisa. Kisaran -128 ... + 128 secara fisik tidak mungkin untuk diwakili dengan 8 bit. Lebar itu hanya mendukung 2 ^ 8 == 256 nilai diskrit, tetapi -128 ... + 128 = 2 * 128 + 1 untuk 0 = 257. Representasi sign-magnitude memungkinkan -127 ... + 127 tetapi memiliki 2 (bipolar) nol Representasi dua-pelengkap mempertahankan nol tunggal tetapi membentuk kisaran dengan memiliki satu nilai lebih di sisi negatif; itu memungkinkan -128 ... + 127. (Dan seterusnya untuk keduanya pada lebar bit yang lebih besar.)
underscore_d

Re komentar saya yang ke-2, masuk akal untuk menganggap kita dapat mengambil pointer ke 1 unsigned charOR atau kemudian melanjutkan menggunakan ++ptrdari sana untuk membaca setiap byte itu ... tapi AFAICT, itu tidak secara khusus didefinisikan sebagai diizinkan, jadi kami dibiarkan untuk menyimpulkan bahwa itu 'mungkin OK' dari banyak bagian lain (dan dalam banyak hal, keberadaan belaka memcpy) dalam Standar, mirip dengan teka-teki gambar. Yang tidak ideal. Yah, mungkin kata-katanya akan membaik pada akhirnya. Inilah masalah CWG yang saya sebutkan tetapi tidak memiliki ruang untuk menautkan - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
underscore_d

@underscore_d maaf, itu salah ketik. [-128, 127] yang saya maksudkan untuk mengetik: hlm. Ya, saya tahu tentang nol ganda ('positif' dan 'negatif' nol) dengan tanda / besarnya. Saya pasti lelah: hal.
RastaJedi

5

unsigned charadalah jantung dari semua tipu daya bit. Di hampir SEMUA kompiler untuk platform SEMUA unsigned charhanyalah sebuah byte dan integer unsigned dari (biasanya) 8 bit yang dapat diperlakukan sebagai integer kecil atau paket bit.

Dalam kecanduan, seperti yang orang lain katakan, standar tidak mendefinisikan tanda char. sehingga Anda memiliki 3 berbeda charjenis: char, signed char, unsigned char.


1
Agak licik, alias sedikit memutar-mutar atau sedikit peretasan memang diketahui menyebabkan kecanduan ;-)
chqrlie

3
0-lah yang menyebabkan masalah. Untuk menghindari kecanduan dari bermain-main, jauhi bit noughty.
DragonLord

5

Jika Anda suka menggunakan berbagai jenis panjang tertentu dan signedness, Anda mungkin lebih baik dengan uint8_t, int8_t, uint16_t, dll hanya karena mereka melakukan apa yang mereka katakan.


4

Beberapa googling menemukan ini , di mana orang berdiskusi tentang ini.

Char unsigned pada dasarnya adalah satu byte. Jadi, Anda akan menggunakan ini jika Anda memerlukan satu byte data (misalnya, mungkin Anda ingin menggunakannya untuk mengaktifkan dan menonaktifkan flag untuk diteruskan ke suatu fungsi, seperti yang sering dilakukan di Windows API).


4

Char yang tidak ditandai menggunakan bit yang dicadangkan untuk tanda char biasa sebagai nomor lain. Ini mengubah rentang ke [0 - 255] sebagai kebalikan dari [-128 - 127].

Umumnya karakter unsigned digunakan ketika Anda tidak ingin tanda. Ini akan membuat perbedaan ketika melakukan hal-hal seperti menggeser bit (pergeseran memperluas tanda) dan hal-hal lain ketika berurusan dengan char sebagai byte daripada menggunakannya sebagai angka.


4

unsigned charhanya mengambil nilai positif: 0 hingga 255 sementara signed charmengambil nilai positif dan negatif: -128 hingga +127.


3

dikutip dari buku "the c programming laugage":

Kualifikasi signedatau unsigneddapat diterapkan ke char atau bilangan bulat apa pun. bilangan unsigned selalu positif atau nol, dan mematuhi hukum modulith aritmetika 2 ^ n, di mana n adalah jumlah bit dalam tipe. Jadi, misalnya, jika karakter adalah 8 bit, variabel karakter yang tidak ditandai memiliki nilai antara 0 dan 255, sedangkan karakter yang ditandatangani memiliki nilai antara -128 dan 127 (dalam mesin komplemen dua.) Apakah karakter karakter yang ditandatangani atau tidak adalah mesin -dependen, tetapi karakter yang dapat dicetak selalu positif.


2

signed chardan unsigned charkeduanya mewakili 1byte, tetapi mereka memiliki rentang yang berbeda.

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

Dalam signed charjika kita mempertimbangkan char letter = 'A', 'A' adalah mewakili biner dari 65 diASCII/Unicode , Jika 65 dapat disimpan, -65 juga dapat disimpan. Tidak ada nilai biner negatif diASCII/Unicode sana karena tidak perlu khawatir tentang nilai negatif.

Contoh

#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;
}

Output -:

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

Signed char(-128) : -128
Unsigned char(-128) : 128
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.