Apakah larangan `panjang` masuk akal?

109

Dalam dunia lintas-platform C ++ (atau C) hari ini kami memiliki :

Data model  | short |   int |   long | long long | pointers/size_t  | Sample operating systems
... 
LLP64/IL32P64   16      32      32     64           64                Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64    16      32      64     64           64                Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...

Apa artinya ini hari ini, adalah untuk integer "umum" (ditandatangani) apa saja, intakan cukup dan mungkin masih dapat digunakan sebagai tipe integer default saat menulis kode aplikasi C ++. Ini juga akan - untuk tujuan praktis saat ini - memiliki ukuran yang konsisten di seluruh platform.

Jika kasus penggunaan memerlukan setidaknya 64 bit, kita dapat menggunakan hari ini long long, meskipun mungkin menggunakan salah satu dari jenis -bitness-menentukan atau __int64jenis mungkin lebih masuk akal.

Ini terletak longdi tengah, dan kami sedang mempertimbangkan untuk langsung melarang penggunaan dari longkode aplikasi kami .

Apakah ini masuk akal , atau adakah kasus untuk menggunakan longkode C ++ (atau C) modern yang harus menjalankan cross platform? (platform menjadi desktop, perangkat seluler, tetapi bukan hal-hal seperti mikrokontroler, DSP, dll.)


Tautan latar belakang yang mungkin menarik:

Martin Ba
sumber
14
Bagaimana Anda menangani panggilan ke perpustakaan yang menggunakan lama?
Ángel
14
longadalah satu-satunya cara untuk menjamin 32 bit. intbisa 16 bit jadi untuk beberapa aplikasi itu tidak cukup. Ya, intkadang-kadang 16 bit pada kompiler modern. Ya, orang memang menulis perangkat lunak pada mikrokontroler. Saya berpendapat lebih banyak orang menulis perangkat lunak yang memiliki lebih banyak pengguna pada mikrokontroler daripada pada PC dengan kebangkitan iPhone dan perangkat Android belum lagi munculnya Arduinos dll.
slebetman
53
Mengapa tidak melarang char, short, int, long, dan long, dan gunakan tipe [u] intXX_t?
immibis
7
@slebetman Saya menggali sedikit lebih dalam, tampaknya persyaratan masih ada, meskipun tersembunyi di §3.9.1.3 di mana standar C ++ menyatakan: "Jenis integer yang ditandatangani dan tidak ditandatangani harus memenuhi batasan yang diberikan dalam standar C, bagian 5.2. 4.2.1. " Dan dalam standar C §5.2.4.2.1 ia menyatakan rentang minimum, persis seperti yang Anda tulis. Anda memang benar. :) Rupanya memiliki salinan standar C ++, tidak cukup, orang perlu menemukan salinan standar C juga.
Tommy Andersen
11
Anda kehilangan dunia DOSBox / Turbo C ++ yang intmasih sangat banyak 16 bit. Saya benci mengatakannya, tetapi jika Anda akan menulis tentang "dunia lintas platform saat ini", Anda tidak dapat mengabaikan seluruh anak benua India.
Lightness Races in Orbit

Jawaban:

17

Satu-satunya alasan yang akan saya gunakan longhari ini adalah ketika memanggil atau mengimplementasikan antarmuka eksternal yang menggunakannya.

Seperti yang Anda katakan dalam posting Anda pendek dan int memiliki karakteristik yang cukup stabil di semua platform desktop / server / mobile utama hari ini dan saya tidak melihat alasan untuk itu berubah di masa mendatang. Jadi saya melihat sedikit alasan untuk menghindarinya secara umum.

longdi sisi lain berantakan. Pada semua sistem 32-bit saya sadar itu memiliki karakteristik sebagai berikut.

  1. Ukurannya persis 32-bit.
  2. Ukurannya sama dengan alamat memori.
  3. Ukurannya sama dengan unit data terbesar yang dapat disimpan dalam register normal dan bekerja dengan satu instruksi.

Sejumlah besar kode ditulis berdasarkan satu atau lebih dari karakteristik ini. Namun dengan pindah ke 64-bit itu tidak mungkin untuk mempertahankan semuanya. Platform Unix-like berlaku untuk LP64 yang mempertahankan karakteristik 2 dan 3 dengan biaya karakteristik 1. Win64 pergi untuk LLP64 yang mempertahankan karakteristik 1 dengan biaya karakteristik 2 dan 3. Hasilnya adalah Anda tidak dapat lagi mengandalkan salah satu dari karakteristik tersebut dan bahwa IMO meninggalkan sedikit alasan untuk digunakan long.

Jika Anda menginginkan jenis yang persis berukuran 32-bit, Anda harus menggunakannya int32_t.

Jika Anda menginginkan jenis yang ukurannya sama dengan pointer, Anda harus menggunakan intptr_t(atau lebih baik uintptr_t).

Jika Anda menginginkan jenis yang merupakan item terbesar yang dapat dikerjakan dalam satu register / instruksi maka sayangnya saya tidak berpikir standar menyediakannya. size_tseharusnya tepat pada platform paling umum tetapi tidak pada x32 .


PS

Saya tidak akan repot dengan tipe "cepat" atau "paling tidak". Jenis "paling tidak" hanya penting jika Anda peduli tentang portabilitas untuk arsitektur yang benar-benar kabur di mana CHAR_BIT != 8. Ukuran tipe "cepat" dalam praktik tampaknya cukup arbiter. Linux tampaknya membuat mereka setidaknya memiliki ukuran yang sama dengan pointer, yang konyol pada platform 64-bit dengan dukungan 32-bit yang cepat seperti x86-64 dan arm64. IIRC iOS membuatnya sekecil mungkin. Saya tidak yakin apa yang dilakukan sistem lain.


PPS

Salah satu alasan untuk menggunakan unsigned long(tetapi tidak jelas long) adalah karena harus memiliki perilaku modulo. Sayangnya karena aturan promosi C kacau jenis unsigned lebih kecil daripada inttidak memiliki perilaku modulo.

Pada semua platform utama saat ini uint32_tadalah ukuran yang sama atau lebih besar dari int dan karenanya memiliki perilaku modulo. Namun ada secara historis dan secara teoritis bisa ada di platform masa depan di mana int64-bit dan karenanya uint32_ttidak memiliki perilaku modulo.

Secara pribadi saya akan mengatakan lebih baik untuk terbiasa memaksakan perilaku modulo dengan menggunakan "1u *" atau "0u +" di awal persamaan Anda karena ini akan bekerja untuk semua ukuran tipe unsigned.

Peter Green
sumber
1
Semua tipe "ukuran tertentu" akan jauh lebih berguna jika mereka dapat menentukan semantik yang berbeda dari tipe bawaan. Sebagai contoh, akan berguna untuk memiliki tipe yang akan menggunakan mod-65536 aritmatika terlepas dari ukuran "int", bersama dengan tipe yang akan mampu menahan angka 0 hingga 65535 tetapi dapat secara sewenang - wenang dan tidak selalu secara konsisten mampu memegang angka lebih besar dari itu. Jenis ukuran apa yang paling cepat akan tergantung pada sebagian besar mesin pada konteksnya, sehingga dapat membiarkan kompiler memilih secara sewenang-wenang akan optimal untuk kecepatan.
supercat
204

Seperti yang Anda sebutkan dalam pertanyaan Anda, perangkat lunak modern adalah semua tentang interoperating antara platform dan sistem di internet. Standar C dan C ++ memberikan rentang untuk ukuran tipe integer, bukan ukuran spesifik (berbeda dengan bahasa seperti Java dan C #).

Untuk memastikan bahwa perangkat lunak Anda dikompilasi pada platform yang berbeda bekerja dengan data yang sama dengan cara yang sama dan untuk memastikan bahwa perangkat lunak lain dapat berinteraksi dengan perangkat lunak Anda menggunakan ukuran yang sama, Anda harus menggunakan bilangan bulat ukuran tetap.

Masukkan <cstdint>yang menyediakan persis itu dan merupakan tajuk standar yang wajib disediakan oleh semua kompiler dan platform pustaka standar. Catatan: header ini hanya diperlukan pada C ++ 11, tetapi banyak implementasi perpustakaan yang lebih lama menyediakannya.

Ingin integer 64 bit yang tidak ditandatangani? Gunakan uint64_t. Menandatangani integer 32 bit? Gunakan int32_t. Sementara jenis-jenis di header adalah opsional, platform modern harus mendukung semua jenis yang didefinisikan dalam header itu.

Terkadang lebar bit spesifik diperlukan, misalnya, dalam struktur data yang digunakan untuk berkomunikasi dengan sistem lain. Lain kali tidak. Untuk situasi yang tidak terlalu ketat, <cstdint>sediakan tipe dengan lebar minimum.

Ada setidaknya varian: int_leastXX_takan menjadi tipe integer minimal XX bit. Ini akan menggunakan tipe terkecil yang menyediakan XX bit, tetapi jenisnya dibiarkan lebih besar dari jumlah bit yang ditentukan. Dalam praktiknya, ini biasanya sama dengan jenis yang dijelaskan di atas yang memberikan jumlah bit yang tepat.

Ada juga varian cepat : int_fastXX_tsetidaknya XX bit, tetapi harus menggunakan tipe yang berkinerja cepat pada platform tertentu. Definisi "cepat" dalam konteks ini tidak ditentukan. Namun, dalam praktiknya, ini biasanya berarti bahwa jenis yang lebih kecil dari ukuran register CPU mungkin alias untuk jenis ukuran register CPU. Misalnya, tajuk Visual C ++ 2015 menetapkan bahwa int_fast16_tbilangan bulat 32 bit karena aritmatika 32 bit secara keseluruhan lebih cepat pada x86 daripada aritmatika 16 bit.

Ini semua penting karena Anda harus dapat menggunakan tipe yang dapat menampung hasil perhitungan yang dilakukan program Anda terlepas dari platform. Jika suatu program menghasilkan hasil yang benar pada satu platform tetapi hasil yang salah pada yang lain karena perbedaan dalam kelebihan integer, itu buruk. Dengan menggunakan tipe integer standar, Anda menjamin bahwa hasil pada platform yang berbeda akan sama berkaitan dengan ukuran integer yang digunakan (tentu saja mungkin ada perbedaan lain antara platform selain lebar integer).

Jadi ya, longharus dilarang dari kode C ++ modern. Jadi harus int, shortdan long long.


sumber
20
Saya berharap saya memiliki lima akun lain untuk lebih memilih ini lagi.
Steven Burnap
4
+1, saya sudah berurusan dengan beberapa kesalahan memori aneh yang hanya terjadi ketika ukuran struct tergantung pada komputer yang Anda kompilasi.
Joshua Snider
9
@Wildcard itu adalah header C yang juga merupakan bagian dari C ++: lihat awalan "c" di atasnya. Ada juga beberapa cara untuk meletakkan typedef di stdnamespace ketika #included di unit kompilasi C ++, tetapi dokumentasi yang saya tautkan tidak menyebutkannya dan Visual Studio tampaknya tidak peduli bagaimana saya mengaksesnya.
11
Larangan intmungkin ... berlebihan? (Saya akan mempertimbangkannya jika kode tersebut harus sangat portabel di semua platform yang tidak jelas (dan tidak begitu tidak jelas). Melarangnya untuk "kode aplikasi" mungkin tidak cocok dengan pengembang kami.
Martin Ba
5
@Snowman #include <cstdint>yang diperlukan untuk menempatkan jenis dalam std::dan (sayangnya) opsional memungkinkan juga untuk menempatkan mereka dalam namespace global. #include <stdint.h>adalah kebalikannya. Hal yang sama berlaku untuk pasangan header C lainnya. Lihat: stackoverflow.com/a/13643019/2757035 Saya berharap Standar hanya mengharuskan masing-masing untuk memengaruhi namespace masing-masing yang diperlukan - daripada tampaknya menyerah pada konvensi yang buruk yang dibuat oleh beberapa implementasi - tetapi oh, baiklah, inilah kita.
underscore_d
38

Tidak, melarang tipe integer bawaan akan menjadi tidak masuk akal. Mereka seharusnya tidak disalahgunakan juga.

Jika Anda membutuhkan integer yang lebar persis N bit, gunakan (atau jika Anda membutuhkan versi). Berpikir sebagai integer 32 bit dan integer 64 bit adalah salah. Ini mungkin terjadi seperti ini pada platform Anda saat ini tetapi ini bergantung pada perilaku yang ditentukan implementasi.std::intN_tstd::uintN_tunsignedintlong long

Menggunakan tipe integer lebar tetap juga berguna untuk inter-operasi dengan teknologi lain. Misalnya, jika beberapa bagian dari aplikasi Anda ditulis dalam Java dan lainnya dalam C ++, Anda mungkin ingin mencocokkan tipe integer sehingga Anda mendapatkan hasil yang konsisten. (Masih perlu diketahui bahwa overflow di Jawa memiliki semantik yang didefinisikan dengan baik sementara signedoverflow di C ++ adalah perilaku yang tidak terdefinisi sehingga konsistensi adalah tujuan yang tinggi.) Mereka juga akan sangat berharga ketika bertukar data antara host komputasi yang berbeda.

Jika Anda tidak membutuhkan tepat N bit, tetapi hanya tipe yang cukup lebar , pertimbangkan untuk menggunakan (dioptimalkan untuk ruang) atau (dioptimalkan untuk kecepatan). Sekali lagi, kedua keluarga memiliki mitra juga.std::int_leastN_tstd::int_fastN_tunsigned

Jadi, kapan harus menggunakan tipe builtin? Nah, karena standar tidak menentukan lebar mereka dengan tepat, gunakan ketika Anda tidak peduli tentang lebar bit yang sebenarnya tetapi tentang karakteristik lainnya.

A charadalah bilangan bulat terkecil yang dapat dialamatkan oleh perangkat keras. Bahasa sebenarnya memaksa Anda untuk menggunakannya untuk alias memori sewenang-wenang. Ini juga satu-satunya jenis yang layak untuk mewakili string karakter (sempit).

Sebuah intbiasanya akan menjadi tipe tercepat mesin dapat menangani. Ini akan cukup lebar sehingga dapat dimuat dan disimpan dengan instruksi tunggal (tanpa harus menutupi atau menggeser bit) dan cukup sempit sehingga dapat dioperasikan dengan (kebanyakan) instruksi perangkat keras yang efisien. Oleh karena itu, intmerupakan pilihan yang sempurna untuk meneruskan data dan melakukan aritmatika ketika melimpah tidak menjadi masalah. Misalnya, jenis pencacahan yang mendasari standar adalah int. Jangan mengubahnya menjadi integer 32 bit hanya karena Anda bisa. Juga, jika Anda memiliki nilai yang hanya bisa –1, 0 dan 1, sebuahintadalah pilihan yang sempurna, kecuali jika Anda akan menyimpan array besar dari mereka dalam hal ini Anda mungkin ingin menggunakan tipe data yang lebih kompak dengan biaya harus membayar harga yang lebih tinggi untuk mengakses elemen individual. Caching yang lebih efisien kemungkinan akan membuahkan hasil untuk ini. Banyak fungsi sistem operasi juga didefinisikan dalam istilah int. Akan konyol untuk mengubah argumen dan hasil mereka bolak-balik. Semua ini bisa dilakukan adalah memperkenalkan kesalahan overflow.

longbiasanya akan menjadi tipe terluas yang dapat ditangani dengan instruksi mesin tunggal. Ini membuat unsigned longsangat menarik untuk berurusan dengan data mentah dan semua jenis manipulasi bit. Sebagai contoh, saya akan berharap untuk melihat unsigned longdalam implementasi bit-vektor. Jika kode ditulis dengan hati-hati, tidak peduli seberapa lebar jenis sebenarnya (karena kode akan beradaptasi secara otomatis). Pada platform di mana kata mesin asli adalah 32 bit, memiliki larik backing dari bit-vektor menjadi larikunsignedBilangan bulat 32 bit paling diinginkan karena akan konyol menggunakan jenis 64 bit yang harus dimuat melalui instruksi mahal hanya untuk menggeser dan menyembunyikan bit yang tidak dibutuhkan lagi. Di sisi lain, jika ukuran kata asli platform adalah 64 bit, saya ingin array jenis itu karena itu berarti bahwa operasi seperti "temukan set pertama" dapat berjalan hingga dua kali lebih cepat. Jadi "masalah" dari longtipe data yang Anda gambarkan, bahwa ukurannya bervariasi dari platform ke platform, sebenarnya adalah fitur yang dapat dimanfaatkan dengan baik. Ini hanya menjadi masalah jika Anda berpikir tentang tipe builtin sebagai tipe dengan lebar bit tertentu, yang sebenarnya tidak.

char, intdan longmerupakan tipe yang sangat berguna seperti dijelaskan di atas. shortdan long longhampir tidak berguna karena semantiknya kurang jelas.

5gon12eder
sumber
4
OP secara khusus menyebut perbedaan ukuran longantara Windows dan Unix. Saya mungkin salah paham, tetapi deskripsi Anda tentang perbedaan ukuran longmenjadi "fitur" bukannya "masalah" masuk akal bagi saya untuk membandingkan model data 32 dan 64 bit, tetapi tidak untuk perbandingan khusus ini. Dalam kasus khusus pertanyaan ini ditanyakan, apakah ini benar-benar fitur? Atau itu fitur dalam situasi lain (yaitu, secara umum), dan tidak berbahaya dalam kasus ini?
Dan Getz
3
@ 5gon12eder: Masalahnya adalah tipe seperti uint32_t dibuat untuk tujuan memungkinkan perilaku kode tidak tergantung pada ukuran "int", tetapi kurangnya jenis yang maknanya "berperilaku seperti uint32_t bekerja pada 32- bit system "membuat penulisan kode yang perilakunya benar secara independen dari ukuran" int "jauh lebih sulit daripada menulis kode yang hampir benar.
supercat
3
Ya, saya tahu ... dari sanalah kutukan itu berasal. Para penulis asli hanya mengambil jalan resistensi sewa karena ketika mereka menulis kode, OS 32-bit lebih dari satu dekade lagi.
Steven Burnap
8
@ 5gon12eder Sedihnya, supercat benar. Semua tipe lebar-tepat adalah "just typedefs" dan aturan promosi bilangan bulat tidak memedulikannya, yang berarti bahwa aritmatika pada uint32_tnilai-nilai akan dilakukan sebagai ditandatangani , intaritmatika lebar pada platform intyang lebih lebar dari uint32_t. (Dengan ABI hari ini, ini sangat mungkin menjadi masalah bagi uint16_t.)
zwol
9
1, terima kasih atas jawaban terinci. Tapi: Oh sayang. Paragraf panjang Anda: " longbiasanya akan menjadi tipe terluas yang dapat ditangani dengan instruksi mesin tunggal ...." - dan ini sangat salah . Lihatlah model data Windows. IMHO, seluruh contoh berikut Anda rusak, karena pada x64 Windows lama masih 32 bit.
Martin Ba
6

Jawaban lain sudah menguraikan jenis cstdint dan variasi yang kurang diketahui di dalamnya.

Saya ingin menambahkan itu:

menggunakan nama tipe khusus domain

Artinya, jangan mendeklarasikan parameter dan variabel Anda menjadi uint32_t(tentu saja tidak long!), Tetapi nama-nama seperti channel_id_type, room_count_typedll.

tentang perpustakaan

Perpustakaan pihak ketiga yang menggunakan longatau yang lainnya dapat mengganggu, terutama jika digunakan sebagai referensi atau petunjuk bagi mereka.

Yang terbaik adalah membuat pembungkus.

Apa strategi saya, secara umum, adalah membuat satu set fungsi seperti pemain yang akan digunakan. Mereka kelebihan beban untuk hanya menerima tipe-tipe yang sama persis dengan tipe yang sesuai, bersama dengan variasi penunjuk dll. Yang Anda butuhkan. Mereka didefinisikan khusus untuk os / compiler / pengaturan. Ini memungkinkan Anda menghapus peringatan dan memastikan hanya konversi "benar" yang digunakan.

channel_id_type cid_out;
...
SomeLibFoo (same_thing_really<int*>(&cid_out));

Khususnya, dengan tipe primitif berbeda yang menghasilkan 32 bit, pilihan Anda tentang cara int32_tdidefinisikan mungkin tidak cocok dengan panggilan pustaka (mis. Int vs long pada Windows).

Fungsi mirip-pemain mendokumentasikan bentrokan, menyediakan waktu kompilasi untuk memeriksa hasil yang cocok dengan parameter fungsi, dan menghilangkan segala peringatan atau kesalahan jika dan hanya jika tipe aktual cocok dengan ukuran sebenarnya yang terlibat. Artinya, itu kelebihan beban dan didefinisikan jika saya melewati (pada Windows) sebuah int*atau a long*dan memberikan kesalahan waktu kompilasi sebaliknya.

Jadi, jika perpustakaan diperbarui atau seseorang mengubah apa yang channel_id_typeada, ini terus diverifikasi.

JDługosz
sumber
mengapa downvote (tanpa komentar)?
JDługosz
Karena sebagian besar downvotes di jaringan ini muncul tanpa komentar ...
Ruslan