Katakanlah saya memiliki satu set bendera, yang dikodekan dalam uint16_t flags
. Sebagai contoh AMAZING_FLAG = 0x02
,. Sekarang, saya punya fungsi. Fungsi ini perlu memeriksa apakah saya ingin mengubah bendera, karena jika saya ingin melakukan itu, saya perlu menulis ke flash. Dan itu mahal. Oleh karena itu, saya ingin cek yang memberi tahu saya apakah flags & AMAZING_FLAG
sama dengan doSet
. Ini adalah ide pertama:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Ini bukan pernyataan intuitif jika. Saya merasa harus ada cara yang lebih baik, seperti:
if ((flags & AMAZING_FLAG) != doSet){
}
Tapi ini tidak benar-benar berfungsi, true
tampaknya sama dengan 0x01
.
Jadi, apakah ada cara yang rapi untuk membandingkan sedikit dengan boolean?
c
gcc
boolean
bitwise-operators
Cheiron
sumber
sumber
(flags & AMAZING_FLAG) && doSet
:?Jawaban:
Untuk mengonversi angka yang bukan nol menjadi 1 (benar), ada trik lama: terapkan
!
operator (bukan) dua kali.sumber
(bool)(flags & AMAZING_FLAG) != doSet
- yang saya temukan lebih langsung. (Meskipun ini menunjukkan bahwa Mr Microsoft memiliki masalah dengan ini.) Juga,((flags & AMAZING_FLAG) != 0)
mungkin persis apa yang dikompilasi dan sangat eksplisit.((bool)(flags & AMAZING_FLAG) != doSet)
memiliki efek yang sama seperti(((flags & AMAZING_FLAG) != 0) != doSet)
dan mereka berdua mungkin akan mengkompilasi ke hal yang persis sama. Yang disarankan(!!(flags & AMAZING_FLAG) != doSet)
adalah setara, dan saya membayangkan akan mengkompilasi ke hal yang sama juga. Ini sepenuhnya masalah selera yang menurut Anda lebih jelas:(bool)
pemeran mengharuskan Anda untuk mengingat bagaimana pemeran dan konversi ke _Bool berfungsi; yang!!
membutuhkan beberapa senam mental lainnya, atau bagi Anda untuk mengetahui "trik".Anda perlu mengonversi bit mask ke pernyataan boolean, yang dalam C sama dengan nilai
0
atau1
.(flags & AMAZING_FLAG) != 0
. Cara paling umum.!!(flags & AMAZING_FLAG)
. Agak umum, juga OK untuk digunakan, tetapi agak samar.(bool)(flags & AMAZING_FLAG)
. Cara C modern dari C99 dan seterusnya.Ambil salah satu dari alternatif di atas, lalu bandingkan dengan boolean Anda menggunakan
!=
atau==
.sumber
Dari sudut pandang logis,
flags & AMAZING_FLAG
hanya sedikit operasi yang menutupi semua flag lainnya. Hasilnya adalah nilai numerik.Untuk menerima ke nilai boolean, Anda akan menggunakan perbandingan
dan sekarang dapat membandingkan nilai logis ini dengan
doSet
.Dalam C mungkin ada singkatan, karena aturan konversi angka ke nilai boolean. Jadi Anda juga bisa menulis
untuk menulis yang lebih singkat. Tetapi versi sebelumnya lebih baik dalam hal keterbacaan.
sumber
Anda dapat membuat topeng berdasarkan
doSet
nilai:Sekarang cek Anda dapat terlihat seperti ini:
Pada beberapa arsitektur,
!!
dapat dikompilasi ke cabang dan dengan ini, Anda mungkin memiliki dua cabang:!!(expr)
doSet
Keuntungan dari proposal saya adalah cabang tunggal yang dijamin.
Catatan: pastikan Anda tidak memperkenalkan perilaku tidak terdefinisi dengan menggeser ke kiri lebih dari 30 (dengan asumsi integer adalah 32 bit). Ini dapat dengan mudah dicapai oleh a
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
sumber
AMAZING_FLAG
dalam halAMAZING_FLAG_IDX
(misalnya#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
), sehingga Anda tidak memiliki data yang sama yang didefinisikan dalam dua tempat seperti yang satu dapat diperbarui (misalnya, dari0x4
ke0x8
) sementara yang lain (IDX
dari2
) yang tersisa tidak berubah .Ada beberapa cara untuk melakukan tes ini:
Operator ternary mungkin menghasilkan lompatan mahal:
Anda juga dapat menggunakan konversi boolean, yang mungkin efisien atau tidak:
Atau alternatifnya yang setara:
Jika multiplikasi murah, Anda dapat menghindari lompatan dengan:
Jika
flags
tidak ditandatangani dan kompilernya sangat pintar, divisi di bawah ini dapat dikompilasi ke perubahan sederhana:Jika arsitektur menggunakan aritmatika komplemen dua, berikut adalah alternatif lain:
Bergantian, orang bisa mendefinisikan
flags
sebagai struktur dengan bitfields dan menggunakan sintaks yang lebih mudah dibaca:Sayangnya, pendekatan ini biasanya disukai karena spesifikasi bitfield tidak memungkinkan kontrol yang tepat atas implementasi bit-level.
sumber