كيف يمكنك ضبط ومسح وتبديل قطعة واحدة؟
-
09-06-2019 - |
سؤال
كيف يمكنك ضبط ومسح وتبديل القليل في C/C++؟
المحلول
الإعداد قليلا
استخدم عامل التشغيل bitwise OR (|
) لتعيين قليلا.
number |= 1UL << n;
من شأنها أن تحدد n
قليلا من number
. n
يجب أن يكون صفرًا، إذا كنت تريد تعيين 1
بت وهكذا حتى تصل n-1
, ، إذا كنت ترغب في تعيين n
قليلا.
يستخدم 1ULL
لو number
هو أوسع من unsigned long
;ترقية ل 1UL << n
لا يحدث إلا بعد التقييم 1UL << n
حيث يكون السلوك غير المحدد هو التحول بأكثر من عرض a long
.وينطبق الشيء نفسه على بقية الأمثلة.
تطهير قليلا
استخدم عامل التشغيل AND (&
) لمسح قليلا.
number &= ~(1UL << n);
سيؤدي ذلك إلى مسح n
قليلا من number
.يجب عليك عكس سلسلة البت باستخدام عامل التشغيل NOT (~
)، ثم وهو.
تبديل قليلا
عامل التشغيل XOR (^
) يمكن استخدامها للتبديل قليلاً.
number ^= 1UL << n;
سيؤدي ذلك إلى تبديل n
قليلا من number
.
التحقق قليلا
أنت لم تطلب هذا، لكن من الأفضل أن أضيفه.
للتحقق قليلاً، قم بإزاحة الرقم n إلى اليمين، ثم إلى اليمين:
bit = (number >> n) & 1U;
من شأنها أن تضع قيمة n
قليلا من number
في المتغير bit
.
تغيير نقليلا ل س
وضع n
قليلا إلى أي منهما 1
أو 0
يمكن تحقيق ذلك من خلال ما يلي في تطبيق C++ المكمل 2:
number ^= (-x ^ number) & (1UL << n);
قليل n
سيتم تعيين إذا x
يكون 1
, ، ومسح إذا x
يكون 0
.لو x
لديه بعض القيمة الأخرى، تحصل على القمامة. x = !!x
سوف تجعلها منطقية إلى 0 أو 1.
لجعل هذا مستقلاً عن سلوك النفي المكمل لـ 2 (حيث -1
تم تعيين كافة البتات، على عكس تكملة 1 أو تنفيذ الإشارة/الحجم C++)، استخدم النفي غير الموقع.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
أو
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
من الجيد عمومًا استخدام الأنواع غير الموقعة لمعالجة البتات المحمولة.
أو
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
سوف يقوم بمسح n
قليلا و (x << n)
سوف يحدد n
قليلا ل x
.
من الجيد أيضًا بشكل عام عدم نسخ/لصق التعليمات البرمجية بشكل عام ويستخدم الكثير من الأشخاص وحدات ماكرو للمعالج المسبق (مثل يجيب مجتمع ويكي على مزيد من الأسفل) أو نوع من التغليف.
نصائح أخرى
باستخدام مكتبة C++ القياسية: std::bitset<N>
.
أو ال يعزز إصدار: boost::dynamic_bitset
.
ليست هناك حاجة للفة بنفسك:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<5> x;
x[1] = 1;
x[2] = 0;
// Note x[0-4] valid
std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010
يسمح إصدار Boost بمجموعة بتات بحجم وقت التشغيل مقارنةً بـ مكتبة قياسية مجموعة بتات بحجم وقت الترجمة.
الخيار الآخر هو استخدام حقول البت:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
يحدد حقل 3 بت (في الواقع، هو ثلاثة حقول 1 بت).أصبحت عمليات البت الآن أبسط قليلاً (هاها):
لتعيين أو مسح قليلا:
mybits.b = 1;
mybits.c = 0;
للتبديل قليلاً:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
التحقق قليلا:
if (mybits.c) //if mybits.c is non zero the next line below will execute
يعمل هذا فقط مع حقول البت ذات الحجم الثابت.وإلا فسيتعين عليك اللجوء إلى تقنيات التلاعب بالبت الموضحة في المشاركات السابقة.
أستخدم وحدات الماكرو المحددة في ملف الرأس للتعامل مع مجموعة البتات ومسحها:
/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1
/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y)) // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
في بعض الأحيان يكون من المفيد استخدام enum
ل اسم البتات:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
ثم استخدم أسماء لاحقا.أي.يكتب
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
لتعيين ومسح واختبار.بهذه الطريقة يمكنك إخفاء الأرقام السحرية عن بقية التعليمات البرمجية الخاصة بك.
بخلاف ذلك أنا أؤيد حل جيريمي.
من قصاصة-c.zipbitops.h:
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/
typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
حسنًا ، دعنا نحلل الأمور ...
التعبير الشائع الذي يبدو أنك تواجه مشاكل معه في كل هذه الأمور هو "(1L << (posn))".كل هذا يفعل هو إنشاء قناع مع جزء واحد على والذي سيعمل مع أي نوع عدد صحيح.تحدد وسيطة "posn" الموضع الذي تريده.إذا posn == 0 ، فسيتم تقييم هذا التعبير إلى:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
إذا posn==8، فسيتم تقييمه إلى:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
بمعنى آخر ، إنه ببساطة ينشئ حقل 0 مع 1 في الموضع المحدد.الجزء الصعب الوحيد هو في الماكرو Bitclr () حيث نحتاج إلى ضبط 0 بت في حقل 1.يتم تحقيق ذلك باستخدام مكمل 1 من نفس التعبير الذي يشير إليه مشغل Tilde (~).
بمجرد إنشاء القناع ، يتم تطبيقه على الوسيطة تمامًا كما تقترح ، عن طريق استخدام مشغلي bitwise و (&) ، أو (|) ، و Xor (^).نظرًا لأن القناع طويل النوع طويل ، ستعمل وحدات الماكرو أيضًا على Char أو Short's أو int's أو Long's.
خلاصة القول هي أن هذا حل عام لفئة كاملة من المشاكل.من الممكن ، بالطبع ، من المناسب إعادة كتابة أي من هذه الماكرو بقيم قناع صريحة في كل مرة تحتاج فيها إلى واحدة ، ولكن لماذا تفعل ذلك؟تذكر أن استبدال الماكرو يحدث في المعالج المسبق ، وبالتالي فإن الكود الذي تم إنشاؤه سيعكس حقيقة أن القيم تعتبر ثابتة من قبل المترجم - أيمن الفعال تمامًا استخدام وحدات الماكرو المعممة "لإعادة اختراع العجلة" في كل مرة تحتاج فيها إلى معالجة بت.
غير مقتنع؟إليك بعض رمز الاختبار - لقد استخدمت Watcom C مع تحسين كامل وبدون استخدام _cdecl ، وبالتالي فإن التفكيك الناتج سيكون نظيفًا قدر الإمكان:
----[ اختبار ج ] ------------------------------------------ -----------------------
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
int bitmanip(int word)
{
word = BitSet(word, 2);
word = BitSet(word, 7);
word = BitClr(word, 3);
word = BitFlp(word, 9);
return word;
}
----[ اختبار الخروج (مفكك)]-------------------------------------- ---------
Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS
Segment: _TEXT BYTE 00000008 bytes
0000 0c 84 bitmanip_ or al,84H ; set bits 2 and 7
0002 80 f4 02 xor ah,02H ; flip bit 9 of EAX (bit 1 of AH)
0005 24 f7 and al,0f7H
0007 c3 ret
No disassembly errors
----[انتهى]------------------------------------------ ----------------------
استخدم عوامل تشغيل bitwise: &
|
لتعيين الجزء الأخير 000b
:
foo = foo | 001b
للتحقق من الجزء الأخير foo
:
if ( foo & 001b ) ....
لمسح الجزء الأخير في foo
:
foo = foo & 110b
إستعملت XXXb
للتوضيح.من المحتمل أنك ستعمل مع تمثيل HEX، اعتمادًا على بنية البيانات التي تقوم بتعبئة البتات فيها.
بالنسبة للمبتدئين أود أن أشرح أكثر قليلا مع مثال:
مثال:
value is 0x55;
bitnum : 3rd.
ال &
يتم استخدام المشغل للتحقق من البت:
0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)
التبديل أو الوجه:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
المشغل أو العامل:تعيين قليلا
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
إليك الماكرو الحسابي المفضل لدي، والذي يعمل مع أي نوع من مصفوفات الأعداد الصحيحة غير الموقعة unsigned char
يصل إلى size_t
(وهو النوع الأكبر الذي يجب أن يكون فعالاً للعمل معه):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
لتعيين قليلا:
BITOP(array, bit, |=);
لتوضيح قليلا:
BITOP(array, bit, &=~);
للتبديل قليلاً:
BITOP(array, bit, ^=);
لاختبار قليلا:
if (BITOP(array, bit, &)) ...
إلخ.
يتمتع نهج bitfield بمزايا أخرى في الساحة المضمنة.يمكنك تحديد بنية يتم تعيينها مباشرةً على البتات الموجودة في سجل جهاز معين.
struct HwRegister {
unsigned int errorFlag:1; // one-bit flag field
unsigned int Mode:3; // three-bit mode field
unsigned int StatusCode:4; // four-bit status code
};
struct HwRegister CR3342_AReg;
يجب أن تكون على دراية بترتيب تعبئة البتات - أعتقد أنه MSB أولاً، ولكن قد يعتمد هذا على التنفيذ.تحقق أيضًا من كيفية تجاوز حقول معالجات المترجم لحدود البايت.
يمكنك بعد ذلك قراءة القيم الفردية وكتابتها واختبارها كما كان من قبل.
نظرًا لأنه تم وضع علامة "مضمن" على هذا، فسأفترض أنك تستخدم وحدة تحكم دقيقة.جميع الاقتراحات المذكورة أعلاه صالحة وتعمل (القراءة والتعديل والكتابة والنقابات والهياكل وما إلى ذلك).
ومع ذلك، خلال فترة من تصحيح الأخطاء المستندة إلى راسم الذبذبات، اندهشت عندما وجدت أن هذه الطرق لها حمل كبير في دورات وحدة المعالجة المركزية مقارنة بكتابة قيمة مباشرة إلى سجلات PORTnSET / PORTnCLEAR الخاصة بالمايكرو، مما يحدث فرقًا حقيقيًا حيث توجد حلقات ضيقة / عالية -تردد دبابيس تبديل ISR.
لأولئك الذين لا يعرفون:في المثال الخاص بي، يحتوي الميكرو على سجل حالة الدبوس العام PORTn الذي يعكس منافذ الإخراج، لذا فإن القيام بـ PORTn |= BIT_TO_SET يؤدي إلى قراءة وتعديل وكتابة لذلك السجل.ومع ذلك، فإن سجلات PORTnSET / PORTnCLEAR تأخذ الرقم "1" ليعني "من فضلك اجعل هذا البت 1" (SET) أو "من فضلك اجعل هذا البت صفرًا" (CLEAR) و"0" يعني "اترك الدبوس بمفرده".لذلك، سينتهي بك الأمر بعنوانين للمنفذ اعتمادًا على ما إذا كنت تقوم بتعيين البت أو مسحه (ليس مناسبًا دائمًا) ولكن أ كثيراً رد فعل أسرع ورمز مجمع أصغر.
بشكل أكثر عمومية، بالنسبة للصور النقطية ذات الحجم العشوائي:
#define BITS 8
#define BIT_SET( p, n) (p[(n)/BITS] |= (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] & (0x80>>((n)%BITS)))
تحقق قليلاً في موقع عشوائي في متغير من النوع التعسفي:
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
استخدام العينة:
int main(void)
{
unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
for (int ix = 0; ix < 64; ++ix)
printf("bit %d is %d\n", ix, bit_test(arr, ix));
return 0;
}
ملحوظات:تم تصميم هذا ليكون سريعًا (نظرًا لمرونته) وغير متفرع.وينتج عنه كود جهاز SPARC فعال عند تجميعه Sun Studio 8؛لقد قمت باختباره أيضًا باستخدام MSVC++ 2008 على AMD64.من الممكن إنشاء وحدات ماكرو مماثلة لإعداد البتات ومسحها.يتمثل الاختلاف الرئيسي بين هذا الحل مقارنة بالعديد من الحلول الأخرى هنا في أنه يعمل في أي موقع وفي أي نوع من المتغيرات تقريبًا.
يهدف هذا البرنامج إلى تغيير أي بت بيانات من 0 إلى 1 أو من 1 إلى 0:
{
unsigned int data = 0x000000F0;
int bitpos = 4;
int bitvalue = 1;
unsigned int bit = data;
bit = (bit>>bitpos)&0x00000001;
int invbitvalue = 0x00000001&(~bitvalue);
printf("%x\n",bit);
if (bitvalue == 0)
{
if (bit == 0)
printf("%x\n", data);
else
{
data = (data^(invbitvalue<<bitpos));
printf("%x\n", data);
}
}
else
{
if (bit == 1)
printf("elseif %x\n", data);
else
{
data = (data|(bitvalue<<bitpos));
printf("else %x\n", data);
}
}
}
إذا كنت تقوم بالكثير من العبث، فقد ترغب في استخدام الأقنعة التي ستجعل الأمر برمته أسرع.الوظائف التالية سريعة جدًا ولا تزال مرنة (فهي تسمح بتدوير البتات في خرائط البت بأي حجم).
const unsigned char TQuickByteMask[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
};
/** Set bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TSetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] |= TQuickByteMask[n]; // Set bit.
}
/** Reset bit in any sized mask.
*
* @return None
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TResetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] &= (~TQuickByteMask[n]); // Reset bit.
}
/** Toggle bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TToggleBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] ^= TQuickByteMask[n]; // Toggle bit.
}
/** Checks specified bit.
*
* @return 1 if bit set else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitSet( short bit, const unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
// Test bit (logigal AND).
if (bitmap[x] & TQuickByteMask[n])
return 1;
return 0;
}
/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitReset( short bit, const unsigned char *bitmap)
{
return TIsBitSet(bit, bitmap) ^ 1;
}
/** Count number of bits set in a bitmap.
*
* @return Number of bits set.
*
* @param bitmap - Pointer to bitmap.
* @param size - Bitmap size (in bits).
*
* @note Not very efficient in terms of execution speed. If you are doing
* some computationally intense stuff you may need a more complex
* implementation which would be faster (especially for big bitmaps).
* See (http://graphics.stanford.edu/~seander/bithacks.html).
*/
int TCountBits( const unsigned char *bitmap, int size)
{
int i, count = 0;
for (i=0; i<size; i++)
if (TIsBitSet(i, bitmap))
count++;
return count;
}
لاحظ أنه لتعيين البت 'n' في عدد صحيح مكون من 16 بت، قم بما يلي:
TSetBit( n, &my_int);
الأمر متروك لك للتأكد من أن رقم البت يقع ضمن نطاق خريطة البت التي تقوم بتمريرها.لاحظ أنه بالنسبة للمعالجات endian الصغيرة التي تقوم وحدات البايت والكلمات والكلمات المزدوجة والكلمات qwords وما إلى ذلك بتعيينها بشكل صحيح لبعضها البعض في الذاكرة (السبب الرئيسي في أن معالجات endian الصغيرة "أفضل" من معالجات endian الكبيرة، آه، أشعر أن حرب اللهب قادمة على...).
استخدم هذا:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
التوسع على bitset
إجابة:
#include <iostream>
#include <bitset>
#include <string>
using namespace std;
int main() {
bitset<8> byte(std::string("10010011");
// Set Bit
byte.set(3); // 10010111
// Clear Bit
byte.reset(2); // 10010101
// Toggle Bit
byte.flip(7); // 00010101
cout << byte << endl;
return 0;
}
إذا كنت ترغب في تنفيذ كل هذه العملية باستخدام برمجة C في ملف نواة لينكس ثم أقترح استخدام واجهات برمجة التطبيقات القياسية لنواة Linux.
يرى https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
set_bit Atomically set a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit and return its old value
test_and_clear_bit Clear a bit and return its old value
test_and_change_bit Change a bit and return its old value
test_bit Determine whether a bit is set
ملحوظة:هنا تتم العملية برمتها في خطوة واحدة.لذلك كل هذه الأمور مضمونة الذري حتى على أجهزة الكمبيوتر SMP ومفيدة للحفاظ على التماسك عبر المعالجات.
يتمتع Visual C 2010، وربما العديد من المترجمين الآخرين، بدعم مباشر لعمليات البت المضمنة.والمثير للدهشة أن هذا يعمل، حتى sizeof()
المشغل يعمل بشكل صحيح.
bool IsGph[256], IsNotGph[256];
// Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
لذا، بالنسبة لسؤالك، IsGph[i] =1
, ، أو IsGph[i] =0
جعل الإعداد ومسح الكتل أمرًا سهلاً.
للعثور على أحرف غير قابلة للطباعة:
// Initialize boolean array to detect UN-printable characters,
// then call function to toggle required bits true, while initializing a 2nd
// boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++) {
if(IsGph[i]) {
IsNotGph[i] = 0;
} else {
IsNotGph[i] = 1;
}
}
لاحظ أنه لا يوجد شيء "خاص" حول هذا الرمز.إنه يعامل قليلاً مثل عدد صحيح - وهو كذلك من الناحية الفنية.عدد صحيح مكون من 1 بت يمكنه الاحتفاظ بقيمتين وقيمتين فقط.
لقد استخدمت هذا الأسلوب ذات مرة للعثور على سجلات القروض المكررة، حيث كان رقم القرض هو مفتاح ISAM، وذلك باستخدام رقم القرض المكون من 6 أرقام كمؤشر في مصفوفة البت.وبسرعة كبيرة، وبعد 8 أشهر، أثبتنا أن نظام الحاسوب المركزي الذي كنا نحصل على البيانات منه كان في الواقع معطلاً.إن بساطة مصفوفات البتات تجعل الثقة في صحتها عالية جدًا - مقابل أسلوب البحث على سبيل المثال.
استخدم أحد العوامل كما هو محدد هنا.
لتعيين قليلا، وتستخدم int x = x | 0x?;
أين ?
هو موضع البت في النموذج الثنائي.
فيما يلي بعض وحدات الماكرو التي أستخدمها:
SET_FLAG(Status, Flag) ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag) ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit) (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask) TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask) TEST_FLAGS(t,ulMask,0)
المتغير المستخدم
int value, pos;
القيمة - البيانات
pos - موضع الجزء الذي نريد تعيينه أو مسحه أو تبديله.
تعيين قليلا:
value = value | 1 << pos;
واضح قليلا:
value = value & ~(1 << pos);
تبديل قليلا:
value = value ^ 1 << pos;
كيف يمكنك ضبط ومسح وتبديل قطعة واحدة؟
لمعالجة مشكلة الترميز الشائعة عند محاولة تشكيل القناع:
1
ليست دائما واسعة بما فيه الكفاية
ما هي المشاكل التي تحدث عندما number
وهو نوع أوسع من 1
?
x
قد يكون كبيرا جدا بالنسبة لهذا التحول 1 << x
يؤدي إلى سلوك غير محدد (يو بي).حتى لو x
ليست كبيرة جدًا، ~
قد لا تقلب ما يكفي من البتات الأكثر أهمية.
// assume 32 bit int/unsigned
unsigned long long number = foo();
unsigned x = 40;
number |= (1 << x); // UB
number ^= (1 << x); // UB
number &= ~(1 << x); // UB
x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough
لضمان 1 واسعة بما فيه الكفاية:
يمكن استخدام الكود 1ull
أو بشكل متحذلق (uintmax_t)1
والسماح للمترجم الأمثل.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
أو الإرسال - مما يؤدي إلى حدوث مشكلات في الترميز/المراجعة/الصيانة مما يحافظ على صحة طاقم الممثلين وتحديثه.
number |= (type_of_number)1 << x;
أو تعزيز بلطف 1
عن طريق فرض عملية حسابية لا يقل عرضها عن نوع number
.
number |= (number*0 + 1) << x;
كما هو الحال مع معظم التلاعبات الصغيرة، من الأفضل العمل بها غير موقعة أنواع بدلا من وقعت تلك
int set_nth_bit(int num, int n){
return (num | 1 << n);
}
int clear_nth_bit(int num, int n){
return (num & ~( 1 << n));
}
int toggle_nth_bit(int num, int n){
return num ^ (1 << n);
}
int check_nth_bit(int num, int n){
return num & (1 << n);
}
نسخة نموذجية من C++ 11 (وضعت في رأس):
namespace bit {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bit) {variable |= ((T1)1 << bit);}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^= ((T1)1 << bit);}
template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}
namespace bitmask {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bits) {variable |= bits;}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
جرب إحدى هذه الوظائف في لغة C لتغيير n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
أو
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
أو
void chang_n_bit(int n, int value)
{
if(value)
bitfield |= 1 << n;
else
bitfield &= ~0 ^ (1 << n);
}
char get_n_bit(int n)
{
return (bitfield & (1 << n)) ? 1 : 0;
}