Di C ++ sizeof('a') == sizeof(char) == 1
,. Ini masuk akal secara intuitif, karena 'a'
merupakan karakter literal, dan sizeof(char) == 1
seperti yang didefinisikan oleh standar.
Namun dalam C sizeof('a') == sizeof(int)
,. Artinya, tampaknya literal karakter C sebenarnya adalah bilangan bulat. Ada yang tahu kenapa? Saya dapat menemukan banyak penyebutan keanehan C ini tetapi tidak ada penjelasan mengapa hal itu ada.
Jawaban:
diskusi tentang subjek yang sama
sumber
char
variabel bukan int, jadi membuat sebuah konstanta karakter menjadi satu adalah kasus khusus. Dan sangat mudah untuk menggunakan nilai karakter tanpa mempromosikannya:c1 = c2;
. OTOH,c1 = 'x'
adalah konversi ke bawah. Yang terpenting,sizeof(char) != sizeof('x')
yang merupakan gangguan bahasa yang serius. Sedangkan untuk konstanta karakter multibyte: itulah alasannya, tapi sudah usang.Pertanyaan aslinya adalah "mengapa?"
Alasannya adalah bahwa definisi karakter literal telah berkembang dan berubah, sambil mencoba untuk tetap kompatibel dengan kode yang ada.
Di hari-hari gelap awal C tidak ada tipe sama sekali. Pada saat saya pertama kali belajar memprogram dalam C, tipe telah diperkenalkan, tetapi fungsi tidak memiliki prototipe untuk memberi tahu pemanggil apa tipe argumennya. Alih-alih itu distandarisasi bahwa semua yang dilewatkan sebagai parameter akan menjadi ukuran int (ini termasuk semua pointer) atau akan menjadi ganda.
Ini berarti bahwa ketika Anda menulis fungsi, semua parameter yang bukan double disimpan di stack sebagai int, tidak peduli bagaimana Anda mendeklarasikannya, dan kompilator meletakkan kode di dalam fungsi untuk menangani ini untuk Anda.
Ini membuat hal-hal menjadi agak tidak konsisten, jadi ketika K&R menulis buku terkenal mereka, mereka memasukkan aturan bahwa literal karakter akan selalu dipromosikan menjadi int dalam ekspresi apa pun, bukan hanya parameter fungsi.
Ketika komite ANSI pertama kali membuat standar C, mereka mengubah aturan ini sehingga karakter literal akan menjadi int, karena ini tampaknya cara yang lebih sederhana untuk mencapai hal yang sama.
Ketika C ++ sedang dirancang, semua fungsi harus memiliki prototipe lengkap (ini masih tidak diperlukan di C, meskipun diterima secara universal sebagai praktik yang baik). Karena itu, diputuskan bahwa literal karakter dapat disimpan dalam karakter. Keuntungan dari ini di C ++ adalah bahwa fungsi dengan parameter char dan fungsi dengan parameter int memiliki tanda tangan yang berbeda. Keuntungan ini tidak terjadi di C.
Inilah mengapa mereka berbeda. Evolusi...
sumber
void f(unsigned char)
Vsvoid f(signed char)
.f('a')
, Anda mungkin ingin resolusi yang berlebihan untuk memilihf(char)
panggilan itu daripadaf(int)
. Ukuran relatifint
danchar
tidak relevan, seperti yang Anda katakan.Saya tidak tahu alasan spesifik mengapa literal karakter di C adalah tipe int. Tapi di C ++, ada alasan bagus untuk tidak pergi ke sana. Pertimbangkan ini:
Anda akan berharap bahwa panggilan untuk mencetak memilih versi kedua yang mengambil karakter. Memiliki karakter literal menjadi int akan membuat itu tidak mungkin. Perhatikan bahwa dalam literal C ++ yang memiliki lebih dari satu karakter masih memiliki tipe int, meskipun nilainya ditentukan dalam implementasi. Jadi,
'ab'
punya tipeint
, sementara'a'
punya tipechar
.sumber
menggunakan gcc di MacBook saya, saya mencoba:
yang ketika dijalankan memberi:
yang menunjukkan bahwa karakter adalah 8 bit, seperti yang Anda duga, tetapi literal karakter adalah int.
sumber
Kembali ketika C sedang ditulis, bahasa assembly MACRO-11 PDP-11 memiliki:
Hal semacam ini cukup umum dalam bahasa assembly - 8 bit rendah akan menampung kode karakter, bit lain dihapus ke 0. PDP-11 bahkan memiliki:
Ini memberikan cara yang mudah untuk memuat dua karakter ke dalam byte rendah dan tinggi dari register 16 bit. Anda kemudian dapat menulisnya di tempat lain, memperbarui beberapa data tekstual atau memori layar.
Jadi, gagasan tentang karakter yang dipromosikan ke ukuran register cukup normal dan diinginkan. Tapi, katakanlah Anda perlu memasukkan 'A' ke dalam register bukan sebagai bagian dari opcode hard-coded, tetapi dari suatu tempat di memori utama yang berisi:
Jika Anda hanya ingin membaca 'A' dari memori utama ini ke dalam register, mana yang akan Anda baca?
Beberapa CPU mungkin hanya secara langsung mendukung pembacaan nilai 16 bit ke dalam register 16 bit, yang berarti pembacaan pada 20 atau 22 kemudian akan membutuhkan bit dari 'X' dihapus, dan tergantung pada endian dari CPU satu atau lainnya perlu beralih ke byte orde rendah.
Beberapa CPU mungkin memerlukan pembacaan yang diselaraskan dengan memori, yang berarti bahwa alamat terendah yang terlibat harus kelipatan dari ukuran data: Anda mungkin dapat membaca dari alamat 24 dan 25, tetapi tidak 27 dan 28.
Jadi, kompiler yang menghasilkan kode untuk mendapatkan 'A' ke dalam register mungkin lebih suka membuang sedikit memori ekstra dan menyandikan nilainya sebagai 0 'A' atau 'A' 0 - tergantung pada endianness, dan juga memastikannya disejajarkan dengan benar ( yaitu tidak pada alamat memori yang aneh).
Dugaan saya adalah bahwa C hanya membawa tingkat perilaku CPU-sentris ini ke atas, memikirkan konstanta karakter yang menempati ukuran register memori, membawa penilaian umum C sebagai "assembler tingkat tinggi".
(Lihat 6.3.3 di halaman 6-25 dari http://www.dmv.net/dec/pdf/macro.pdf )
sumber
Saya ingat membaca K&R dan melihat potongan kode yang akan membaca karakter pada satu waktu sampai mencapai EOF. Karena semua karakter adalah karakter yang valid untuk berada dalam aliran file / input, ini berarti EOF tidak dapat berupa nilai karakter apa pun. Apa yang kode itu lakukan adalah memasukkan karakter yang sudah dibaca ke dalam int, lalu menguji EOF, lalu mengubahnya menjadi karakter jika tidak.
Saya menyadari ini tidak benar-benar menjawab pertanyaan Anda, tetapi akan masuk akal jika literal karakter lainnya menjadi sizeof (int) jika literal EOF adalah.
sumber
Saya belum melihat alasan untuk itu (C char literals adalah tipe int), tetapi inilah yang dikatakan Stroustrup tentang hal itu (dari Design and Evolution 11.2.1 - Fine-Grain Resolution):
Jadi untuk sebagian besar, itu seharusnya tidak menimbulkan masalah.
sumber
Alasan historis untuk ini adalah bahwa C, dan pendahulunya B, awalnya dikembangkan pada berbagai model minikomputer DEC PDP dengan berbagai ukuran kata, yang mendukung ASCII 8-bit tetapi hanya dapat melakukan aritmatika pada register. (Bukan PDP-11, bagaimanapun; itu datang kemudian.) Versi awal C didefinisikan
int
sebagai ukuran kata asli mesin, dan nilai apa pun yang lebih kecil dari yangint
diperlukan untuk dilebarkan keint
agar dapat diteruskan ke atau dari suatu fungsi , atau digunakan dalam ekspresi bitwise, logis atau aritmatika, karena begitulah cara perangkat keras yang mendasarinya bekerja.Itu juga mengapa aturan promosi bilangan bulat masih mengatakan bahwa tipe data apa pun yang lebih kecil dari satu
int
akan dipromosikanint
. Implementasi C juga diperbolehkan untuk menggunakan matematika pelengkap satu daripada pelengkap dua untuk alasan historis yang serupa. Alasan pelolosan karakter oktal dan konstanta oktal adalah warga kelas satu dibandingkan dengan heksa juga karena minikomputer DEC awal memiliki ukuran kata yang dapat dibagi menjadi potongan tiga-byte tetapi bukan camilan empat-byte.sumber
char
panjangnya tepat 3 angka oktalIni adalah perilaku yang benar, yang disebut "promosi integral". Ini dapat terjadi dalam kasus lain juga (terutama operator biner, jika saya ingat dengan benar).
EDIT: Hanya untuk memastikan, saya memeriksa salinan Expert C Programming: Deep Secrets , dan saya mengonfirmasi bahwa literal karakter tidak dimulai dengan tipe int . Ini awalnya bertipe char tetapi ketika digunakan dalam ekspresi , itu dipromosikan menjadi int . Berikut kutipan dari buku tersebut:
sumber
Saya tidak tahu, tapi saya akan menebak lebih mudah menerapkannya seperti itu dan itu tidak terlalu penting. Tidak sampai C ++ ketika tipe dapat menentukan fungsi mana yang akan dipanggil yang perlu diperbaiki.
sumber
Saya memang tidak tahu ini. Sebelum prototipe ada, sesuatu yang lebih sempit dari int diubah menjadi int saat menggunakannya sebagai argumen fungsi. Itu mungkin bagian dari penjelasannya.
sumber
char
keint
akan membuat konstanta karakter tidak diperlukan menjadi int. Yang relevan adalah bahasa memperlakukan konstanta karakter secara berbeda (dengan memberinya tipe yang berbeda) darichar
variabel, dan yang dibutuhkan adalah penjelasan tentang perbedaan itu.Ini hanya bersinggungan dengan spesifikasi bahasa, tetapi dalam perangkat keras CPU biasanya hanya memiliki satu ukuran register - 32 bit, katakanlah - dan kapan pun ia benar-benar berfungsi pada char (dengan menambahkan, mengurangi, atau membandingkannya) ada sebuah konversi implisit menjadi int ketika itu dimuat ke register. Kompilator menangani dengan benar masking dan menggeser nomor setelah setiap operasi sehingga jika Anda menambahkan, katakanlah, 2 ke (unsigned char) 254, itu akan membungkus ke 0 bukan 256, tetapi di dalam silikon itu benar-benar int sampai Anda menyimpannya kembali ke memori.
Ini semacam poin akademis karena bahasa tersebut bisa saja menentukan tipe literal 8-bit, tetapi dalam kasus ini spesifikasi bahasa mencerminkan lebih dekat apa yang sebenarnya dilakukan CPU.
(x86 wonks mungkin mencatat bahwa ada, misalnya, addh op asli yang menambahkan register lebar-pendek dalam satu langkah, tetapi di dalam inti RISC ini diterjemahkan menjadi dua langkah: tambahkan angka, lalu perpanjang tanda, seperti pasangan add / extsh pada PowerPC)
sumber
char
variabel memiliki tipe yang berbeda. Promosi otomatis, yang mencerminkan perangkat keras, tidak relevan - sebenarnya anti-relevan, karenachar
variabel secara otomatis dipromosikan sehingga bukan alasan literal karakter tidak bertipechar
. Alasan sebenarnya adalah literal multibyte, yang sekarang sudah usang.