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, int
akan 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 __int64
jenis mungkin lebih masuk akal.
Ini terletak long
di tengah, dan kami sedang mempertimbangkan untuk langsung melarang penggunaan dari long
kode aplikasi kami .
Apakah ini masuk akal , atau adakah kasus untuk menggunakan long
kode 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:
- Apa standar C ++ yang menyatakan ukuran int, tipe panjang menjadi?
- Mengapa tim Win64 memilih model LLP64?
- Model Pemrograman 64-Bit: Mengapa LP64? (agak tua)
- Apakah
long
dijamin setidaknya 32 bit? (Ini membahas diskusi komentar di bawah ini. Jawab .)
sumber
long
adalah satu-satunya cara untuk menjamin 32 bit.int
bisa 16 bit jadi untuk beberapa aplikasi itu tidak cukup. Ya,int
kadang-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.int
masih 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.Jawaban:
Satu-satunya alasan yang akan saya gunakan
long
hari 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.
long
di sisi lain berantakan. Pada semua sistem 32-bit saya sadar itu memiliki karakteristik sebagai berikut.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 baikuintptr_t
).Jika Anda menginginkan jenis yang merupakan item terbesar yang dapat dikerjakan dalam satu register / instruksi maka sayangnya saya tidak berpikir standar menyediakannya.
size_t
seharusnya 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 jelaslong
) adalah karena harus memiliki perilaku modulo. Sayangnya karena aturan promosi C kacau jenis unsigned lebih kecil daripadaint
tidak memiliki perilaku modulo.Pada semua platform utama saat ini
uint32_t
adalah 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 manaint
64-bit dan karenanyauint32_t
tidak 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.
sumber
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? Gunakanint32_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_t
akan 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_t
setidaknya 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 bahwaint_fast16_t
bilangan 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,
long
harus dilarang dari kode C ++ modern. Jadi harusint
,short
danlong long
.sumber
std
namespace ketika#include
d di unit kompilasi C ++, tetapi dokumentasi yang saya tautkan tidak menyebutkannya dan Visual Studio tampaknya tidak peduli bagaimana saya mengaksesnya.int
mungkin ... 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.#include <cstdint>
yang diperlukan untuk menempatkan jenis dalamstd::
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.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_t
std::uintN_t
unsigned
int
long 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
signed
overflow 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_t
std::int_fastN_t
unsigned
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
char
adalah 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
int
biasanya 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,int
merupakan pilihan yang sempurna untuk meneruskan data dan melakukan aritmatika ketika melimpah tidak menjadi masalah. Misalnya, jenis pencacahan yang mendasari standar adalahint
. Jangan mengubahnya menjadi integer 32 bit hanya karena Anda bisa. Juga, jika Anda memiliki nilai yang hanya bisa –1, 0 dan 1, sebuahint
adalah 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 istilahint
. Akan konyol untuk mengubah argumen dan hasil mereka bolak-balik. Semua ini bisa dilakukan adalah memperkenalkan kesalahan overflow.long
biasanya akan menjadi tipe terluas yang dapat ditangani dengan instruksi mesin tunggal. Ini membuatunsigned long
sangat menarik untuk berurusan dengan data mentah dan semua jenis manipulasi bit. Sebagai contoh, saya akan berharap untuk melihatunsigned long
dalam 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 larikunsigned
Bilangan 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" darilong
tipe 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
,int
danlong
merupakan tipe yang sangat berguna seperti dijelaskan di atas.short
danlong long
hampir tidak berguna karena semantiknya kurang jelas.sumber
long
antara Windows dan Unix. Saya mungkin salah paham, tetapi deskripsi Anda tentang perbedaan ukuranlong
menjadi "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?uint32_t
nilai-nilai akan dilakukan sebagai ditandatangani ,int
aritmatika lebar pada platformint
yang lebih lebar dariuint32_t
. (Dengan ABI hari ini, ini sangat mungkin menjadi masalah bagiuint16_t
.)long
biasanya 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.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 tidaklong
!), Tetapi nama-nama sepertichannel_id_type
,room_count_type
dll.tentang perpustakaan
Perpustakaan pihak ketiga yang menggunakan
long
atau 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.
Khususnya, dengan tipe primitif berbeda yang menghasilkan 32 bit, pilihan Anda tentang cara
int32_t
didefinisikan 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 along*
dan memberikan kesalahan waktu kompilasi sebaliknya.Jadi, jika perpustakaan diperbarui atau seseorang mengubah apa yang
channel_id_type
ada, ini terus diverifikasi.sumber