Apakah aman menggunakan -1 untuk mengatur semua bit menjadi true?

132

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 0xffffffffatau ~0lebih baik?

hyperlogic
sumber
1
Saya tidak mengerti, bisa tolong jelaskan?
corazza
8
Saya pikir apakah arti kode itu jelas adalah pertanyaan yang paling penting. Meskipun -1akan 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 menggunakan 0xffffffffbukan untuk portabilitas atau kebenaran, tetapi untuk kejelasan.
Cam Jackson
@CamJackson komentarnya tidak dan siapa pun yang menulis kode C bisa terbiasa dengan bagaimana nilai diwakili.
Miles Rout
Pertanyaan awalnya ditandai dengan benar sebagai C dan C ++. Bahasa mungkin berbeda karena C ++ memiliki proposal yang membutuhkan pelengkap dua bahasa. Yang mengatakan, itu tidak mengubah fakta yang -1tetap menjadi solusi portabel dan terbelakang untuk kedua bahasa, tetapi itu dapat mempengaruhi beberapa alasan dalam jawaban lain.
Adrian McCarthy

Jawaban:

154

Saya sarankan Anda untuk melakukannya persis seperti yang Anda tunjukkan, karena ini adalah yang paling mudah. Inisialisasi -1yang 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 tinggi unsigned.

Untuk contoh kemungkinan kejutan, pertimbangkan yang ini:

unsigned long a = ~0u;

Itu tidak serta merta menyimpan pola dengan semua bit 1 menjadi a. Tapi itu pertama-tama akan membuat pola dengan semua bit 1 dalam unsigned int, dan kemudian menetapkannya a. Apa yang terjadi ketika unsigned longmemiliki lebih banyak bit adalah bahwa tidak semua itu adalah 1.

Dan pertimbangkan yang ini, yang akan gagal pada representasi komplemen bukan-dua:

unsigned int a = ~0; // Should have done ~0u !

Alasan untuk itu adalah bahwa ~0harus membalikkan semua bit. Melawan yang akan menghasilkan -1pada mesin pelengkap dua (yang merupakan nilai yang kita butuhkan!), Tetapi tidak akan menghasilkan -1representasi lain. Pada mesin pelengkap seseorang, hasilnya nol. Jadi, pada mesin komplemen seseorang, yang di atas akan menginisialisasi ake 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 ake nilai setinggi mungkin, adalah -1atau UINT_MAX. Yang kedua akan tergantung pada jenis a- Anda harus menggunakan ULONG_MAXuntuk unsigned long. Namun, yang pertama tidak akan tergantung pada jenisnya, dan itu cara yang bagus untuk mendapatkan nilai tertinggi.

Kita tidak berbicara tentang apakah -1semua bit satu (tidak selalu demikian). Dan kita tidak berbicara tentang apakah ~0semuanya memiliki bit (tentu saja).

Tetapi yang kita bicarakan adalah apa hasil dari flagsvariabel yang diinisialisasi . Dan untuk itu, hanya-1 akan bekerja dengan setiap jenis dan mesin.

Johannes Schaub - litb
sumber
9
mengapa -1 dijamin untuk dikonversi ke semua yang? Apakah itu dijamin oleh standar?
jalf
9
konversi yang terjadi adalah bahwa itu berulang kali menambahkan satu lebih dari ULONG_MAX sampai dalam jangkauan (6.3.1.3 dalam konsep C TC2). Dalam C ++ itu sama, hanya menggunakan cara lain memformalkannya (modulo 2 ^ n). Itu semua bermuara pada hubungan matematika.
Johannes Schaub - litb
6
@ litb: casting -1 tentu saja merupakan cara yang bagus untuk mendapatkan nilai maksimal yang tidak ditandatangani, tetapi ini tidak terlalu deskriptif; itulah alasan mengapa konstanta _MAX ada (SIZE_MAX ditambahkan dalam C99); diberikan, C ++ versi numeric_limits<size_t>::max()agak bertele-tele, tapi begitu adalah pemain ...
Christoph
11
"Kami tidak berbicara tentang apakah -1 memiliki semua bit satu (tidak selalu memiliki). Dan kami tidak berbicara tentang apakah ~ 0 memiliki semua bit satu (tentu saja)." -- apa??? Saya pikir intinya adalah mengatur semua bit menjadi 1. Begitulah cara kerja flag..tidak ?? Anda melihat bit . Siapa yang peduli dengan nilainya?
mpen
14
@Mark si penanya peduli. Dia bertanya "Apakah aman menggunakan -1 untuk mengatur semua bit menjadi true". Ini tidak bertanya tentang apa bit -1diwakili oleh, juga tidak bertanya apa yang bit ~0miliki. 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 ~0mungkin tidak -1, tapi ini adalah nilai yang Anda butuhkan. Lihat jawaban saya dan ringkasan @ Dingo.
Johannes Schaub - litb
49
  • 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.

Dingo
sumber
10
Bagaimana ~ 0 (yaitu operator komplemen seseorang) bergantung pada representasi komplemen dua?
Drew Hall
11
Anda memiliki ini mundur. Pengaturannya menandai ke -1 yang bergantung pada representasi pelengkap dua. Dalam representasi tanda + magnitudo minus satu hanya memiliki dua bit yang ditetapkan: bit tanda dan bit magnitudo paling signifikan.
Stephen C. Steel
15
Standar C mensyaratkan bahwa nilai int dari nol memiliki bit tanda dan semua bit nilai adalah nol. Setelah pelengkap seseorang, semua bit itu adalah satu. Nilai sebuah int dengan semua bit yang ditetapkan adalah: Tanda-dan-besarnya: INT_MIN Komplemen One: -0 Komplemen dua: -1 Jadi pernyataan "unsigned int flags = ~ 0;" akan menetapkan nilai apa pun di atas yang cocok dengan representasi integer platform. Tetapi '-1' dari komplemen dua adalah satu-satunya yang akan mengatur semua bit bendera menjadi satu.
Dingo
9
@Stephen: Menyetujui representasi. Tetapi ketika nilai int ditugaskan untuk int unsigned, unsigned tidak mendapatkan nilainya dengan mengadopsi representasi internal nilai int (kecuali dalam sistem dua komplemen tempat umumnya bekerja). Semua nilai yang ditugaskan ke int unsigned adalah modulo (UINT_MAX + 1), jadi penugasan -1 berfungsi tidak peduli representasi internal.
Dingo
20
@ Mark: Anda membingungkan dua operasi. ~0menghasilkan intnilai dengan semua bit yang ditetapkan, tentu saja. Tetapi menetapkan suatu intke unsigned inttidak 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, menetapkan intnilai negatif untuk unsigned inthasil 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.
Steve Jessop
25

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.

Doug T.
sumber
Ada beberapa kasus di mana Anda tidak terlalu peduli, tetapi jarang terjadi. Misalnya algoritma yang bekerja pada dataset N bit dengan memecahnya menjadi potongan sizeof (unsigned) * CHAR_BIT masing-masing.
MSalters
2
+1. Bahkan jika ukuran datatype lebih besar dari # dari F (yaitu, Anda tidak cukup mengatur semua bit menjadi true), karena Anda secara eksplisit mengatur nilainya, Anda setidaknya menyadari bit mana yang "aman" untuk menggunakan "..
mpen
17

Cara yang menghindari masalah yang disebutkan adalah dengan hanya melakukan:

unsigned int flags = 0;
flags = ~flags;

Portable dan to the point.

hammar
sumber
2
Tetapi kemudian Anda kehilangan kemampuan untuk menyatakan flagssebagai const.
David Stone
1
@DavidStoneunsigned int const flags = ~0u;
@Zoidberg '- Itu gagal bekerja pada sistem selain komplemen dua. Misalnya, pada sistem tanda-magnitudo, ~0adalah bilangan bulat yang memiliki semua bit diatur ke 1, tetapi ketika Anda menetapkan itu intke unsignedvariabel flags, Anda melakukan konversi nilai dari -2**31(dengan asumsi 32-bit int) ke (-2**31 % 2**32) == 2**31, yang merupakan bilangan bulat dengan semua bit tetapi set pertama ke 1.
David Stone
Itu juga alasan mengapa jawaban ini berbahaya. Sepertinya jawaban @Zoidberg '- akan sama, tetapi sebenarnya tidak. Namun, sebagai orang yang membaca kode, saya harus memikirkannya untuk memahami mengapa Anda mengambil dua langkah untuk menginisialisasi, dan mungkin tergoda untuk mengubahnya menjadi satu langkah.
David Stone
2
Ah ya, saya tidak melihat uakhiran dalam jawaban Anda. Itu tentu saja akan berhasil, tetapi masih memiliki masalah menentukan tipe data yang Anda gunakan ( unsigneddan tidak lebih besar) dua kali, yang dapat menyebabkan kesalahan. Kesalahan kemungkinan besar akan muncul jika penugasan dan deklarasi variabel awal terpisah lebih jauh.
David Stone
13

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 karena 0xffffffffmengasumsikan bahwa unsigned int adalah integer 32-bit.

Edouard A.
sumber
Saya suka ini karena standarnya, tetapi terlalu bertele-tele dan itu membuat Anda menyatakan jenisnya dua kali. Menggunakan ~ 0 mungkin lebih aman karena 0 bisa berupa tipe integer apa pun. (Meskipun saya sadar baunya terlalu banyak dari C.)
Macke
Fakta bahwa itu bertele-tele dapat dilihat sebagai keuntungan. Tapi saya suka ~ 0 juga.
Edouard A.
2
Anda dapat mengurangi keringkasan dengan makro UINT_MAX standar, karena Anda tetap meng-hardcoding jenis unsigned int int.
2
@Acke Anda dapat menghindari menyebutkan tipe C ++ 11 dengan auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone
11
unsigned int flags = -1;  // all bits are true

"Apakah ini cara portabel yang bagus untuk mencapai ini?"

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,

unsigned int flags = static_cast<unsigned int>(-1);

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:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Semuanya benar dan lebih jelas bagi sesama programmer Anda.

Dan dengan C ++ 11 : Kita dapat menggunakan autountuk membuat semua ini lebih sederhana:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Saya menganggap benar dan jelas lebih baik daripada hanya benar.

Adrian McCarthy
sumber
10

Mengonversi -1 menjadi jenis apa pun yang tidak ditandatangani dijamin oleh standar untuk menghasilkan semua-yang. Penggunaan ~0Uumumnya buruk karena 0memiliki tipe unsigned intdan tidak akan mengisi semua bit dari tipe unsigned yang lebih besar, kecuali jika Anda secara eksplisit menulis sesuatu seperti ~0ULL. Pada sistem waras, ~0harus identik dengan -1, tetapi karena standar memungkinkan representasi satu-pelengkap dan sign / magnitude, sebenarnya tidak portabel.

Tentu saja selalu baik-baik saja untuk menulis 0xffffffffjika 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 batas UINT_MAX, ULONG_MAX, ULLONG_MAX, dll

Secara pribadi saya selalu menggunakan -1. Itu selalu berhasil dan Anda tidak perlu memikirkannya.

R .. GitHub BERHENTI MEMBANTU ICE
sumber
FWIW, jika saya maksudkan “semua 1 bit” yang saya gunakan ~(type)0(yah, isi typesaja 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.
Donal Fellows
6
@ Donal: Anda salah besar. C menetapkan bahwa, ketika mengonversi nilai yang tidak cocok dengan tipe yang tidak ditandatangani, nilai tersebut dikurangi modulo 2 ^ n di mana n adalah jumlah bit dalam tipe tujuan. Ini berlaku untuk nilai yang ditandatangani dan tipe yang tidak ditandatangani yang lebih besar. Ini tidak ada hubungannya dengan dua pelengkap.
R .. GitHub BERHENTI MEMBANTU ICE
2
Mereka melakukan menerapkannya sama, yang sepele; Anda hanya menggunakan opcode pengurangan yang tidak ditandatangani alih-alih yang sudah ditandatangani atau neginstruksi. 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.
R .. GitHub BERHENTI MEMBANTU ICE
1
@ R .: Kasing var = ~(0*var)akan gagal karena jenis varyang tidak ditandatangani lebih sempit dari int. Mungkin var = ~(0U*var)? (secara pribadi saya masih lebih suka -1,).
caf
1
Jawaban ini jauh lebih jelas daripada jawaban Johannes Schaub. Namun, menetapkan integer literal negatif ke tipe yang tidak ditandatangani tanpa gips biasanya menghasilkan peringatan kompiler. Menekan peringatan memerlukan gips, yang berarti harus memperhatikan jenis target, jadi Anda juga dapat menggunakan UINT_MAX atau ULONG_MAX dan lebih jelas daripada mengandalkan detail kecil dalam standar yang membingungkan banyak rekan programmer Anda. .
Adrian McCarthy
5

Selama Anda memiliki #include <limits.h>salah satu termasuk Anda, Anda harus menggunakan

unsigned int flags = UINT_MAX;

Jika Anda ingin nilai bit yang panjang, Anda bisa menggunakannya

unsigned long flags = ULONG_MAX;

Nilai-nilai ini dijamin memiliki semua bit nilai dari hasil yang ditetapkan ke 1, terlepas dari bagaimana bilangan bulat yang ditandatangani diimplementasikan.

Michael Norrish
sumber
1
konstanta yang Anda sarankan sebenarnya didefinisikan dalam Limit.h - stdint.h berisi batas untuk tipe integer tambahan (integer berukuran tetap, intptr_t, ...)
Christoph
5

Iya. Seperti disebutkan dalam jawaban lain, -1adalah yang paling portabel; Namun, itu tidak terlalu semantik dan memicu peringatan kompiler.

Untuk mengatasi masalah ini, coba penolong sederhana ini:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Pemakaian:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;
Diamond Python
sumber
3
Sebagai seorang programmer C, kode seperti ini memberi saya mimpi buruk di malam hari.
jforberg
Dan apa yang terjadi ketika Anda menggunakan ini dalam konteks seperti di ALL_BITS_TRUE ^ amana ainteger yang ditandatangani? Jenis tetap ditandatangani integer dan pola bit (representasi objek) tergantung pada target yang menjadi pelengkap 2 atau tidak.
Peter Cordes
Tidak, ALL_BITS_TRUE ^ amemberikan kesalahan kompilasi karena ALL_BITS_TRUEambigu. Namun bisa digunakan uint32_t(ALL_BITS_TRUE) ^ a. Anda dapat mencobanya sendiri di cpp.sh :) Saat ini saya akan menambahkan static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");di operatoruntuk memastikan pengguna tidak mencoba menggunakan int(ALL_BITS_TRUE). Saya akan memperbarui jawabannya.
Diamond Python
3

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_tjenis.

myron-semack
sumber
2

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:

mov rax, 0ffffffffffffffffh

Berikut ini menempatkan 64 1s di RAX:

mov rax, 0ffffffffh

Hanya untuk kelengkapan, berikut ini menempatkan 32 1 di bagian bawah RAX (alias EAX):

mov eax, 0ffffffffh

Dan sebenarnya saya punya program gagal ketika saya ingin menulis 0xffffffff ke variabel 64-bit dan saya mendapat 0xffffffffffffffff. Dalam C ini akan menjadi:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

hasilnya adalah:

x is 0xffffffffffffffff

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.

Nathan Fellman
sumber
1
Selamat, Anda telah menemukan bug penyusun!
tc.
Apakah ini didokumentasikan di mana saja sebagai bug?
Nathan Fellman
1
Dengan asumsi UINT64_C(0xffffffff)mengembang ke sesuatu seperti 0xffffffffuLL, itu pasti bug kompiler. Standar C sebagian besar membahas nilai , nilai yang diwakili oleh 0xffffffffadalah 4294967295 (bukan 36893488147419103231), dan tidak ada konversi ke tipe integer yang ditandatangani yang terlihat.
tc.
2

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):

Representasi tipe integral harus mendefinisikan nilai dengan menggunakan sistem numerasi biner murni. [Catatan 44:] Representasi posisi untuk bilangan bulat yang menggunakan angka biner 0 dan 1, di mana nilai yang diwakili oleh bit berturut-turut adalah aditif, mulai dengan 1, dan dikalikan dengan kekuatan integral berturut-turut dari 2, kecuali mungkin untuk bit dengan posisi tertinggi.

Itu meninggalkan kemungkinan salah satu bit sama sekali.

James Hopkin
sumber
Secara umum, kami tidak dapat yakin tentang nilai bit padding. Dan jika kita mau, maka kita bisa berada dalam bahaya karena kita bisa menghasilkan representasi perangkap untuk mereka (dan itu bisa membangkitkan sinyal). Namun, std membutuhkan unsigned char untuk tidak memiliki bit padding, dan pada 4.7 / 2 dalam standar c ++ mengatakan bahwa mengkonversi integer ke tipe unsigned, nilai dari variabel unsigned yang dihasilkan adalah nilai terkecil yang kongruen dengan nilai integer sumber, (modulo 2 ^ n, n == jumlah-bit dalam tipe yang tidak ditandatangani). lalu (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1 memiliki semua bit yang diatur dalam sistem penomoran biner murni.
Johannes Schaub - litb
Jika kita benar - benar ingin memiliki semua bit 1 dalam representasi objek dari tipe yang tidak ditandatangani, kita perlu memset. Tapi kita bisa menghasilkan representasi perangkap dengan demikian :( Bagaimanapun, sebuah implementasi mungkin tidak memiliki alasan untuk membuang sedikit dari bilangan bulat yang tidak ditandatangani mereka sehingga akan menggunakannya untuk menyimpan nilai-nilainya. Tetapi Anda telah mendapatkan poin yang sangat bagus - tidak ada yang menghentikan interpretasi dari memiliki omong kosong konyol beberapa bit, saya pikir (terpisah dari dalam char / ditandatangani Char / unsigned char, yang tidak harus memiliki orang-orang) 1 tentu saja :).
Johannes Schaub - litb
Pada akhirnya, saya pikir standar bisa lebih jelas representasi apa yang dimaksud dalam 4.7 / 2. Jika mengacu pada representasi objek, maka tidak ada lagi tempat untuk melapisi bit (saya telah melihat orang berdebat seperti itu yang saya tidak melihat ada yang salah dengan). Tapi saya pikir itu berbicara tentang representasi nilai (karena semuanya dalam 4.7 / 2 adalah tentang nilai pula - dan kemudian padding bit dapat bersarang di sebelah bit nilai.
Johannes Schaub - litb
1
Standar tampaknya cukup jelas memiliki representasi '2's, 1's melengkapi dan menandatangani besaran' dalam pikiran, tetapi tidak ingin mengesampingkan apa pun. Poin menarik tentang menjebak representasi juga. Sejauh yang saya tahu, bit yang saya kutip adalah definisi 'sistem bilangan biner murni' sejauh yang terkait dengan Standar - bit 'kecuali' pada akhirnya adalah satu-satunya keraguan saya mengenai apakah casting -1 dijamin untuk kerja.
James Hopkin
2

Meskipun 0xFFFF(atau 0xFFFFFFFF, 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 typedefnilai 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 dalam int, tetapi akan cocok dalam sebuah unsigned int. Jika an intadalah 16 bit, maka longvar &= ~0x4000;atau longvar &= ~0x10000; akan menghapus satu bit longvar, tetapi longvar &= ~0x8000;akan menghapus bit 15 dan semua bit di atas itu. Nilai-nilai yang cocok intakan memiliki operator komplemen diterapkan pada suatu jenis int, tetapi hasilnya akan diperpanjang untuk long, pengaturan bit atas. Nilai yang terlalu besar untuk unsigned intakan memiliki operator pelengkap diterapkan untuk mengetik long. Nilai-nilai yang berada di antara ukuran-ukuran itu, bagaimanapun, akan menerapkan operator pelengkap untuk mengetik unsigned int, yang kemudian akan dikonversi untuk mengetik longtanpa ekstensi tanda.

supercat
sumber
1

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.

Drew Hall
sumber
6
Saya mengatakan itu juga. Tetapi kemudian saya menyadari bahwa saya salah, karena -1 yang ditandatangani selalu mengkonversi ke max_value yang tidak ditandatangani di bawah aturan konversi, terlepas dari representasi nilai. Setidaknya, tidak di C ++, saya tidak punya standar C untuk tangan.
Steve Jessop
6
ada ambiguitas. -1 bukan 0xFFFFFFFF. Tapi -1 adalah 0xFFFFFFFF jika dikonversi ke int unsigned (memiliki 32 bit). Itulah yang membuat diskusi ini begitu sulit menurut saya. Banyak orang memiliki hal yang sangat berbeda dalam pikiran ketika mereka berbicara tentang string kecil itu.
Johannes Schaub - litb
1

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:

std::bitset<32> const flags(-1);

Ini akan selalu mengandung jumlah bit yang tepat yang Anda butuhkan. Itu membangun a std::bitsetdengan semua bit diatur ke 1 untuk alasan yang sama disebutkan dalam jawaban lain.

David Stone
sumber
0

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.

Zifre
sumber
4
"0xFF ... tidak baik karena tergantung pada lebar jenis" Yang merupakan satu-satunya cara yang waras, saya pikir. Anda seharusnya mendefinisikan dengan jelas apa arti setiap flag / bit dalam program Anda. Jadi, jika Anda menentukan bahwa Anda menggunakan 32 bit terendah untuk menyimpan flag, Anda harus membatasi diri Anda untuk menggunakan 32 bit tersebut, apakah ukuran sebenarnya dari int adalah 32 atau 64.
Juan Pablo Califano
0

Saya katakan:

int x;
memset(&x, 0xFF, sizeof(int));

Ini akan selalu memberi Anda hasil yang diinginkan.

Alex
sumber
4
Bukan pada sistem dengan karakter 9-bit!
tc.
0

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:

  1. Sertakan <limits>dan gunakanstd::numeric_limits< your_type >::max()
  2. Tulis fungsi templated khusus (Ini juga akan memungkinkan beberapa pemeriksaan kewarasan, yaitu jika jenis tujuan benar-benar jenis yang tidak ditandai)

Tujuannya dapat menambah lebih banyak kejelasan, karena penugasan -1akan selalu membutuhkan komentar penjelasan.

Antonio
sumber
0

Cara untuk membuat maknanya sedikit lebih jelas namun untuk menghindari pengulangan jenis:

const auto flags = static_cast<unsigned int>(-1);
FlorianH
sumber
-6

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 () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

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

ankit sablok
sumber
9
Dua komentar: pertama, Anda salah tentang ukuran bilangan bulat pada mesin "paling". Kedua, ur txt sangat sulit dibaca karena 2 dari kami jenis bahasa sekkrit. Tolong jelaskan kami bahasa Inggris .
Konrad Rudolph