Saya telah melihat pola ini banyak digunakan dalam C & C ++.
unsigned int flags = -1; // all bits are true
Apakah ini cara portabel yang baik untuk mencapai ini? Atau menggunakan 0xffffffff
atau ~0
lebih baik?
c++
c
binary
bit-fields
hyperlogic
sumber
sumber
-1
akan selalu berfungsi, fakta bahwa komentar diperlukan setelah itu menunjukkan bahwa itu bukan kode yang jelas. Jika variabel dimaksudkan sebagai kumpulan flag, mengapa menetapkannya bilangan bulat? Jenisnya bisa berupa bilangan bulat, tetapi jelas bukan bilangan bulat semantik. Anda tidak akan menambah atau mengalikannya. Jadi saya akan menggunakan0xffffffff
bukan untuk portabilitas atau kebenaran, tetapi untuk kejelasan.-1
tetap menjadi solusi portabel dan terbelakang untuk kedua bahasa, tetapi itu dapat mempengaruhi beberapa alasan dalam jawaban lain.Jawaban:
Saya sarankan Anda untuk melakukannya persis seperti yang Anda tunjukkan, karena ini adalah yang paling mudah. Inisialisasi
-1
yang akan selalu bekerja , terlepas dari representasi tanda yang sebenarnya, sementara~
kadang-kadang akan memiliki perilaku yang mengejutkan karena Anda harus memiliki jenis operan yang tepat. Hanya dengan begitu Anda akan mendapatkan nilai jenis yang paling tinggiunsigned
.Untuk contoh kemungkinan kejutan, pertimbangkan yang ini:
Itu tidak serta merta menyimpan pola dengan semua bit 1 menjadi
a
. Tapi itu pertama-tama akan membuat pola dengan semua bit 1 dalamunsigned int
, dan kemudian menetapkannyaa
. Apa yang terjadi ketikaunsigned long
memiliki lebih banyak bit adalah bahwa tidak semua itu adalah 1.Dan pertimbangkan yang ini, yang akan gagal pada representasi komplemen bukan-dua:
Alasan untuk itu adalah bahwa
~0
harus membalikkan semua bit. Melawan yang akan menghasilkan-1
pada mesin pelengkap dua (yang merupakan nilai yang kita butuhkan!), Tetapi tidak akan menghasilkan-1
representasi lain. Pada mesin pelengkap seseorang, hasilnya nol. Jadi, pada mesin komplemen seseorang, yang di atas akan menginisialisasia
ke nol.Hal yang harus Anda pahami adalah itu semua tentang nilai - bukan bit. Variabel diinisialisasi dengan nilai . Jika dalam initializer Anda memodifikasi bit variabel yang digunakan untuk inisialisasi, nilai akan dihasilkan sesuai dengan bit tersebut. Nilai yang Anda butuhkan, untuk menginisialisasi
a
ke nilai setinggi mungkin, adalah-1
atauUINT_MAX
. Yang kedua akan tergantung pada jenisa
- Anda harus menggunakanULONG_MAX
untukunsigned long
. Namun, yang pertama tidak akan tergantung pada jenisnya, dan itu cara yang bagus untuk mendapatkan nilai tertinggi.Kita tidak berbicara tentang apakah
-1
semua bit satu (tidak selalu demikian). Dan kita tidak berbicara tentang apakah~0
semuanya memiliki bit (tentu saja).Tetapi yang kita bicarakan adalah apa hasil dari
flags
variabel yang diinisialisasi . Dan untuk itu, hanya-1
akan bekerja dengan setiap jenis dan mesin.sumber
numeric_limits<size_t>::max()
agak bertele-tele, tapi begitu adalah pemain ...-1
diwakili oleh, juga tidak bertanya apa yang bit~0
miliki. Kami mungkin tidak peduli dengan nilai-nilai, tetapi kompiler tidak. Kami tidak dapat mengabaikan fakta bahwa operasi bekerja dengan dan berdasarkan nilai. The nilai dari~0
mungkin tidak-1
, tapi ini adalah nilai yang Anda butuhkan. Lihat jawaban saya dan ringkasan @ Dingo.unsigned int flags = -1;
portabel.unsigned int flags = ~0;
tidak portabel karena bergantung pada representasi dua-pelengkap.unsigned int flags = 0xffffffff;
tidak portabel karena mengasumsikan int 32-bit.Jika Anda ingin mengatur semua bit dengan cara yang dijamin oleh standar C, gunakan yang pertama.
sumber
~0
menghasilkanint
nilai dengan semua bit yang ditetapkan, tentu saja. Tetapi menetapkan suatuint
keunsigned int
tidak harus menghasilkan int unsigned memiliki pola bit yang sama dengan pola bit yang ditandatangani. Hanya dengan representasi komplemen 2's yang selalu demikian. Pada komplemen 1s atau representasi sign-magnitude, menetapkanint
nilai negatif untukunsigned int
hasil dalam pola bit yang berbeda. Ini karena standar C ++ mendefinisikan ditandatangani -> konversi yang tidak ditandatangani menjadi nilai modulo-equal, bukan nilai dengan bit yang sama.Terus terang saya pikir semua fff lebih mudah dibaca. Mengenai komentar yang merupakan antipattern, jika Anda benar-benar peduli bahwa semua bit diatur / dihapus, saya berpendapat bahwa Anda mungkin berada dalam situasi di mana Anda peduli tentang ukuran variabel, yang akan membutuhkan sesuatu seperti dorongan :: uint16_t, dll.
sumber
Cara yang menghindari masalah yang disebutkan adalah dengan hanya melakukan:
Portable dan to the point.
sumber
flags
sebagaiconst
.unsigned int const flags = ~0u;
~0
adalah bilangan bulat yang memiliki semua bit diatur ke 1, tetapi ketika Anda menetapkan ituint
keunsigned
variabelflags
, Anda melakukan konversi nilai dari-2**31
(dengan asumsi 32-bitint
) ke(-2**31 % 2**32) == 2**31
, yang merupakan bilangan bulat dengan semua bit tetapi set pertama ke 1.u
akhiran dalam jawaban Anda. Itu tentu saja akan berhasil, tetapi masih memiliki masalah menentukan tipe data yang Anda gunakan (unsigned
dan tidak lebih besar) dua kali, yang dapat menyebabkan kesalahan. Kesalahan kemungkinan besar akan muncul jika penugasan dan deklarasi variabel awal terpisah lebih jauh.Saya tidak yakin menggunakan int unsigned untuk flag adalah ide yang bagus di C ++. Bagaimana dengan bitset dan sejenisnya?
std::numeric_limit<unsigned int>::max()
lebih baik karena0xffffffff
mengasumsikan bahwa unsigned int adalah integer 32-bit.sumber
auto
.auto const flags = std::numeric_limit<unsigned>::max()
.Portabel? Ya .
Baik? Luar biasa , sebagaimana dibuktikan oleh semua kebingungan yang ditampilkan di utas ini. Menjadi cukup jelas sehingga sesama programmer Anda dapat memahami kode tanpa kebingungan harus menjadi salah satu dimensi yang kami ukur untuk kode yang baik.
Juga, metode ini rentan terhadap peringatan kompiler . Untuk menghilangkan peringatan tanpa melumpuhkan kompiler Anda, Anda membutuhkan pemeran eksplisit. Sebagai contoh,
Pemeran eksplisit mengharuskan Anda memperhatikan jenis target. Jika Anda memperhatikan jenis target, maka Anda secara alami akan menghindari jebakan dari pendekatan lain.
Saran saya adalah memperhatikan jenis target dan memastikan tidak ada konversi tersirat. Sebagai contoh:
Semuanya benar dan lebih jelas bagi sesama programmer Anda.
Dan dengan C ++ 11 : Kita dapat menggunakan
auto
untuk membuat semua ini lebih sederhana:Saya menganggap benar dan jelas lebih baik daripada hanya benar.
sumber
Mengonversi -1 menjadi jenis apa pun yang tidak ditandatangani dijamin oleh standar untuk menghasilkan semua-yang. Penggunaan
~0U
umumnya buruk karena0
memiliki tipeunsigned int
dan tidak akan mengisi semua bit dari tipe unsigned yang lebih besar, kecuali jika Anda secara eksplisit menulis sesuatu seperti~0ULL
. Pada sistem waras,~0
harus identik dengan-1
, tetapi karena standar memungkinkan representasi satu-pelengkap dan sign / magnitude, sebenarnya tidak portabel.Tentu saja selalu baik-baik saja untuk menulis
0xffffffff
jika Anda tahu Anda membutuhkan 32 bit, tetapi -1 memiliki keuntungan bahwa ia akan bekerja dalam konteks apa pun bahkan ketika Anda tidak tahu ukuran jenisnya, seperti makro yang bekerja pada banyak jenis. , atau jika ukuran jenis bervariasi berdasarkan implementasi. Jika Anda mengetahui jenis, cara yang aman lain untuk mendapatkan semua-yang adalah macro batasUINT_MAX
,ULONG_MAX
,ULLONG_MAX
, dllSecara pribadi saya selalu menggunakan -1. Itu selalu berhasil dan Anda tidak perlu memikirkannya.
sumber
~(type)0
(yah, isitype
saja tentu saja). Casting nol masih menghasilkan nol, jadi itu jelas, dan meniadakan semua bit dalam tipe target cukup jelas. Tidak sesering itu saya benar-benar menginginkan operasi itu; YMMV.neg
instruksi. Mesin yang memiliki perilaku aritmatika yang ditandatangani palsu memiliki opcode aritmatika yang ditandatangani / tidak ditandatangani terpisah. Tentu saja kompiler yang sangat bagus hanya akan selalu mengabaikan opcode yang ditandatangani bahkan untuk nilai yang ditandatangani dan karenanya mendapatkan dua komplemen secara gratis.var = ~(0*var)
akan gagal karena jenisvar
yang tidak ditandatangani lebih sempit dariint
. Mungkinvar = ~(0U*var)
? (secara pribadi saya masih lebih suka-1
,).Selama Anda memiliki
#include <limits.h>
salah satu termasuk Anda, Anda harus menggunakanJika Anda ingin nilai bit yang panjang, Anda bisa menggunakannya
Nilai-nilai ini dijamin memiliki semua bit nilai dari hasil yang ditetapkan ke 1, terlepas dari bagaimana bilangan bulat yang ditandatangani diimplementasikan.
sumber
Iya. Seperti disebutkan dalam jawaban lain,
-1
adalah yang paling portabel; Namun, itu tidak terlalu semantik dan memicu peringatan kompiler.Untuk mengatasi masalah ini, coba penolong sederhana ini:
Pemakaian:
sumber
ALL_BITS_TRUE ^ a
manaa
integer yang ditandatangani? Jenis tetap ditandatangani integer dan pola bit (representasi objek) tergantung pada target yang menjadi pelengkap 2 atau tidak.ALL_BITS_TRUE ^ a
memberikan kesalahan kompilasi karenaALL_BITS_TRUE
ambigu. Namun bisa digunakanuint32_t(ALL_BITS_TRUE) ^ a
. Anda dapat mencobanya sendiri di cpp.sh :) Saat ini saya akan menambahkanstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
dioperator
untuk memastikan pengguna tidak mencoba menggunakanint(ALL_BITS_TRUE)
. Saya akan memperbarui jawabannya.Saya tidak akan melakukan hal -1. Ini agak tidak intuitif (setidaknya bagi saya). Menugaskan data yang ditandatangani ke variabel yang tidak ditandatangani sepertinya merupakan pelanggaran terhadap tatanan alami hal-hal.
Dalam situasi Anda, saya selalu menggunakan
0xFFFF
. (Gunakan angka Fs yang tepat untuk ukuran variabel, tentu saja.)[BTW, saya sangat jarang melihat trik -1 dilakukan dalam kode dunia nyata.]
Selain itu, jika Anda benar-benar peduli tentang bit individu dalam vairable, itu akan menjadi ide yang baik untuk mulai menggunakan fixed-lebar
uint8_t
,uint16_t
,uint32_t
jenis.sumber
Pada prosesor IA-32 Intel, boleh saja menulis 0xFFFFFFFF ke register 64-bit dan mendapatkan hasil yang diharapkan. Ini karena IA32e (ekstensi 64-bit ke IA32) hanya mendukung 32-bit dengan segera. Dalam instruksi 64-bit, 32-bit langsung menandatangani diperluas ke 64-bit.
Berikut ini adalah ilegal:
Berikut ini menempatkan 64 1s di RAX:
Hanya untuk kelengkapan, berikut ini menempatkan 32 1 di bagian bawah RAX (alias EAX):
Dan sebenarnya saya punya program gagal ketika saya ingin menulis 0xffffffff ke variabel 64-bit dan saya mendapat 0xffffffffffffffff. Dalam C ini akan menjadi:
hasilnya adalah:
Saya pikir memposting ini sebagai komentar untuk semua jawaban yang mengatakan bahwa 0xFFFFFFFF mengasumsikan 32 bit, tetapi begitu banyak orang yang menjawabnya, saya pikir saya akan menambahkannya sebagai jawaban yang terpisah.
sumber
UINT64_C(0xffffffff)
mengembang ke sesuatu seperti0xffffffffuLL
, itu pasti bug kompiler. Standar C sebagian besar membahas nilai , nilai yang diwakili oleh0xffffffff
adalah 4294967295 (bukan 36893488147419103231), dan tidak ada konversi ke tipe integer yang ditandatangani yang terlihat.Lihat jawaban litb untuk penjelasan yang sangat jelas tentang masalah ini.
Ketidaksepakatan saya adalah bahwa, pada dasarnya, tidak ada jaminan untuk kedua kasus tersebut. Saya tidak tahu ada arsitektur yang tidak mewakili nilai yang tidak ditandatangani dari 'satu kurang dari dua dengan kekuatan jumlah bit' karena semua bit ditetapkan, tetapi inilah yang dikatakan oleh Standar (3.9.1 / 7 plus catatan 44):
Itu meninggalkan kemungkinan salah satu bit sama sekali.
sumber
Meskipun
0xFFFF
(atau0xFFFFFFFF
, dll.) Mungkin lebih mudah dibaca, ini dapat merusak portabilitas dalam kode yang seharusnya portabel. Pertimbangkan, misalnya, rutinitas pustaka untuk menghitung berapa banyak item dalam struktur data yang memiliki bit tertentu yang ditetapkan (bit yang tepat ditentukan oleh pemanggil). Rutin mungkin benar-benar agnostik untuk mewakili bit, tetapi masih perlu memiliki konstanta "semua bit". Dalam kasus seperti itu, -1 akan jauh lebih baik daripada konstanta hex karena akan bekerja dengan ukuran bit apa pun.Kemungkinan lain, jika suatu
typedef
nilai digunakan untuk bitmask, adalah menggunakan ~ (bitMaskType) 0; jika bitmask kebetulan hanya tipe 16-bit, ekspresi itu hanya akan memiliki 16 bit yang ditetapkan (bahkan jika 'int' akan menjadi 32 bit) tetapi karena 16 bit adalah semua yang diperlukan, semuanya harus baik-baik saja asalkan satu sebenarnya menggunakan tipe yang sesuai dalam typecast.Kebetulan, ekspresi dari bentuk
longvar &= ~[hex_constant]
memiliki gotcha yang buruk jika konstanta hex terlalu besar untuk ditampung dalamint
, tetapi akan cocok dalam sebuahunsigned int
. Jika anint
adalah 16 bit, makalongvar &= ~0x4000;
ataulongvar &= ~0x10000
; akan menghapus satu bitlongvar
, tetapilongvar &= ~0x8000;
akan menghapus bit 15 dan semua bit di atas itu. Nilai-nilai yang cocokint
akan memiliki operator komplemen diterapkan pada suatu jenisint
, tetapi hasilnya akan diperpanjang untuklong
, pengaturan bit atas. Nilai yang terlalu besar untukunsigned int
akan memiliki operator pelengkap diterapkan untuk mengetiklong
. Nilai-nilai yang berada di antara ukuran-ukuran itu, bagaimanapun, akan menerapkan operator pelengkap untuk mengetikunsigned int
, yang kemudian akan dikonversi untuk mengetiklong
tanpa ekstensi tanda.sumber
Praktis: Ya
Secara teoritis: Tidak.
-1 = 0xFFFFFFFF (atau ukuran berapapun int pada platform Anda) hanya benar dengan aritmatika komplemen dua. Dalam praktiknya, ini akan berhasil, tetapi ada mesin legacy di luar sana (mainframe IBM, dll.) Tempat Anda mendapatkan sedikit tanda yang sebenarnya daripada representasi pelengkap dua. Solusi ~ 0 yang Anda usulkan harus bekerja di mana-mana.
sumber
Seperti yang disebutkan orang lain, -1 adalah cara yang benar untuk membuat integer yang akan dikonversi ke tipe yang tidak ditandai dengan semua bit diatur ke 1. Namun, hal yang paling penting dalam C ++ adalah menggunakan tipe yang benar. Karena itu, jawaban yang benar untuk masalah Anda (yang mencakup jawaban untuk pertanyaan yang Anda ajukan) adalah ini:
Ini akan selalu mengandung jumlah bit yang tepat yang Anda butuhkan. Itu membangun a
std::bitset
dengan semua bit diatur ke 1 untuk alasan yang sama disebutkan dalam jawaban lain.sumber
Ini tentu aman, karena -1 akan selalu memiliki semua bit yang tersedia, tetapi saya suka ~ 0 lebih baik. -1 hanya tidak masuk akal untuk sebuah
unsigned int
.0xFF
... tidak baik karena tergantung pada lebar jenisnya.sumber
Saya katakan:
Ini akan selalu memberi Anda hasil yang diinginkan.
sumber
Memanfaatkan fakta bahwa menetapkan semua bit ke satu untuk tipe yang tidak ditandatangani sama dengan mengambil nilai maksimum yang mungkin untuk tipe yang diberikan,
dan memperluas cakupan pertanyaan ke semua tipe integer yang tidak ditandatangani :
Menetapkan -1 berfungsi untuk semua tipe integer yang tidak ditandatangani (unsigned int, uint8_t, uint16_t, dll.) Untuk C dan C ++.
Sebagai alternatif, untuk C ++, Anda dapat:
<limits>
dan gunakanstd::numeric_limits< your_type >::max()
Tujuannya dapat menambah lebih banyak kejelasan, karena penugasan
-1
akan selalu membutuhkan komentar penjelasan.sumber
Cara untuk membuat maknanya sedikit lebih jelas namun untuk menghindari pengulangan jenis:
sumber
ya representasi yang ditampilkan sangat benar seolah-olah kita melakukannya sebaliknya Anda akan memerlukan operator untuk membalikkan semua bit tetapi dalam hal ini logikanya cukup mudah jika kita mempertimbangkan ukuran bilangan bulat dalam mesin
misalnya dalam kebanyakan mesin integer adalah 2 byte = 16 bit nilai maksimum yang dapat ditampungnya adalah 2 ^ 16-1 = 65535 2 ^ 16 = 65536
0% 65536 = 0 -1% 65536 = 65535 yang sesuai dengan 1111 ............. 1 dan semua bit diatur ke 1 (jika kita mempertimbangkan kelas residu mod 65536) maka itu jauh lurus ke depan.
saya kira
Tidak, jika Anda mempertimbangkan gagasan ini, itu sempurna untuk ints unsigned dan itu benar-benar berhasil
cukup periksa fragmen program berikut
int main () {
}
jawaban untuk b = 4294967295 yang merupakan -1% 2 ^ 32 pada bilangan bulat 4 byte
oleh karena itu sangat valid untuk bilangan bulat tak bertanda
dalam hal terjadi perbedaan laporan plzz
sumber