C: العمليات على البتات
العمليات على البتات كالتحكم في صف من مفاتيح الإضاءة — كل مصباح يقابل بتًا واحدًا، ويمكنك تشغيل أو إيقاف أو قلب أي مصباح دون التأثير على البقية.
1. مراجعة النظام الثنائي
تمثل الحواسيب جميع البيانات داخليًا بالنظام الثنائي. بايت واحد = 8 بتات، كل بت إما 0 أو 1.
Decimal 42 = Binary 00101010
Decimal 255 = Binary 11111111
الأعداد الصحيحة الموقّعة تستخدم تمثيل مكمل الاثنين: البت الأعلى هو بت الإشارة، 0 للموجب، و1 للسالب.
Decimal 5 = 00000101
Decimal -5 = 11111011 (two's complement)
2. عاملات البت الستة
| العامل | الاسم | القاعدة |
|---|---|---|
& |
AND على البتات | النتيجة 1 فقط عندما يكون كلا البتين 1 |
| |
OR على البتات | النتيجة 1 عندما يكون أحد البتين على الأقل 1 |
^ |
XOR على البتات | النتيجة 1 عندما يختلف البتان |
~ |
NOT على البتات | 0 يصبح 1، و1 يصبح 0 |
<< |
إزاحة لليسار | يزيح البتات لليسار، يملأ البتات الدنيا بـ 0 |
>> |
إزاحة لليمين | يزيح البتات لليمين، يملأ البتات العليا ببت الإشارة (موقّع) أو 0 (غير موقّع) |
(1) AND على البتات (&)
1010 (10)
& 1100 (12)
------
1000 (8)
الاستخدامات: تصفير بتات محددة، التحقق مما إذا كانت بتات معينة تساوي 1.
(2) OR على البتات (|)
1010 (10)
| 1100 (12)
------
1110 (14)
الاستخدامات: ضبط بتات محددة إلى 1.
(3) XOR على البتات (^)
1010 (10)
^ 1100 (12)
------
0110 (6)
خصائص: a ^ a = 0، a ^ 0 = a. XOR قابل للعكس — تطبيقه مرتين بنفس القيمة يعيد الأصل.
(4) NOT على البتات (~)
~ 00101010 (42)
= 11010101 (-43, two's complement)
للأعداد غير الموقّعة: ~0 = 255 (8 بت)، ~0 = 0xFFFFFFFF (32 بت).
(5) الإزاحة لليسار (<<)
5 << 2
= 00000101 << 2
= 00010100
= 20
الإزاحة لليسار بمقدار n تكافئ الضرب في 2^n. 5 << 2 = 5 * 4 = 20.
(6) الإزاحة لليمين (>>)
20 >> 2
= 00010100 >> 2
= 00000101
= 5
الإزاحة لليمين بمقدار n تكافئ القسمة على 2^n (تُقرّب نحو الصفر).
3. عمليات البت الأربع الأساسية
(1) ضبط بت (إلى 1)
flags |= (1 << n);
يضبط البت n إلى 1، مع ترك جميع البتات الأخرى دون تغيير.
(2) تصفير بت (إلى 0)
flags &= ~(1 << n);
يُنتج ~(1 << n) قناعًا كله 1 ما عدا 0 في الموضع n. AND يُصفّر تلك البت فقط.
(3) قلب بت
flags ^= (1 << n);
عملية XOR: 0^1=1، 1^1=0، مما ينفذ القلب بشكل مثالي.
(4) فحص بت
if (flags & (1 << n)) {
}
إذا كانت البت n تساوي 1، فالنتيجة غير صفرية؛ وإذا كانت 0، فالنتيجة صفر.
▶ مثال
#include <stdio.h>
void print_bits(unsigned char val) {
for (int i = 7; i >= 0; i--) {
printf("%d", (val >> i) & 1);
}
printf("\n");
}
int main(void) {
unsigned char flags = 0;
flags |= (1 << 3);
printf("Set bit 3: ");
print_bits(flags);
flags |= (1 << 5);
printf("Set bit 5: ");
print_bits(flags);
flags &= ~(1 << 3);
printf("Clear bit 3: ");
print_bits(flags);
flags ^= (1 << 5);
printf("Toggle bit 5: ");
print_bits(flags);
flags ^= (1 << 7);
printf("Toggle bit 7: ");
print_bits(flags);
if (flags & (1 << 7)) {
printf("Bit 7 is 1\n");
}
return 0;
}
Set bit 3: 00001000
Set bit 5: 00101000
Clear bit 3: 00100000
Toggle bit 5: 00000000
Toggle bit 7: 10000000
Bit 7 is 1
4. تقنيات القناع
القناع هو تركيبة محددة مسبقًا من البتات تُستخدم لاستخراج أو تعديل حقول بت محددة ضمن البيانات.
(1) استخراج البتات الدنيا
unsigned int val = 0xABCD;
unsigned int low_byte = val & 0xFF;
0xFF قناع يحتفظ بأدنى 8 بتات فقط.
(2) استخراج البتات العليا
unsigned int high_byte = (val >> 8) & 0xFF;
إزاحة لليمين بـ 8 بتات أولاً، ثم قناع للحصول على أدنى 8 بتات.
(3) دمج القيم
unsigned int combined = (high << 8) | low;
دمج بايتين في قيمة 16 بت.
▶ مثال
استخراج قيم ألوان RGB. قيمة اللون 24 بت تحتوي على 8 بتات لكل من الأحمر والأخضر والأزرق:
#include <stdio.h>
int main(void) {
unsigned int color = 0xFF6633;
unsigned char r = (color >> 16) & 0xFF;
unsigned char g = (color >> 8) & 0xFF;
unsigned char b = color & 0xFF;
printf("Color #FF6633:\n");
printf(" Red: %d\n", r);
printf(" Green: %d\n", g);
printf(" Blue: %d\n", b);
unsigned int new_color = 0x00;
new_color |= ((r / 2) << 16);
new_color |= ((g / 2) << 8);
new_color |= (b / 2);
printf("Darkened: #%06X\n", new_color);
return 0;
}
Color #FF6633:
Red: 255
Green: 102
Blue: 51
Darkened: #7F3319
5. أعلام الصلاحيات
صلاحيات ملفات لينكس هي تطبيق كلاسيكي للعمليات على البتات. 9 بتات صلاحية تمثل المالك/المجموعة/الآخرين للقراءة/الكتابة/التنفيذ:
rwxr-xr-x = 111101101 = 0755
rw-r--r-- = 110100100 = 0644
#include <stdio.h>
#define READ (1 << 2)
#define WRITE (1 << 1)
#define EXECUTE (1 << 0)
void show_permission(unsigned char perm) {
printf("%c", (perm & READ) ? 'r' : '-');
printf("%c", (perm & WRITE) ? 'w' : '-');
printf("%c", (perm & EXECUTE) ? 'x' : '-');
}
int main(void) {
unsigned char owner = READ | WRITE | EXECUTE;
unsigned char group = READ | EXECUTE;
unsigned char other = READ | EXECUTE;
printf("Permissions: ");
show_permission(owner);
show_permission(group);
show_permission(other);
printf("\n");
owner &= ~WRITE;
printf("After removing write: ");
show_permission(owner);
show_permission(group);
show_permission(other);
printf("\n");
return 0;
}
Permissions: rwxr-xr-x
After removing write: r-xr-xr-x
6. حيل عملية على البتات
(1) تبديل متغيرين (بدون متغير مؤقت)
a ^= b;
b ^= a;
a ^= b;
المبدأ: خاصية الانعكاس الذاتي لـ XOR. لكن هذا يضر بالقراءة ولا يُنصح به في التطوير الفعلي.
(2) فحص الفردي والزوجي
if (n & 1) {
}
إذا كانت أدنى بت 1، فالعدد فردي. هذا أسرع من n % 2.
(3) الضرب/القسمة بأسس 2
n << 1
n << 2
n >> 1
المترجمات عادةً تحسّن n * 2 إلى إزاحة، لكن الإزاحة آمنة فقط للأعداد الموجبة.
(4) حساب أسس 2
unsigned int pow2 = 1u << n;
1u << 0 = 1، 1u << 1 = 2، 1u << 8 = 256... الإزاحة أسرع بكثير من pow(2, n).
(5) المحاذاة إلى أس 2
unsigned int aligned = (value + mask) & ~mask;
مثلاً، المحاذاة إلى حد 4 بايت: (n + 3) & ~3.
#include <stdio.h>
int main(void) {
int values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < 10; i++) {
int aligned = (values[i] + 3) & ~3;
printf("%d -> %d\n", values[i], aligned);
}
return 0;
}
0 -> 0
1 -> 4
2 -> 4
3 -> 4
4 -> 4
5 -> 8
6 -> 8
7 -> 8
8 -> 8
9 -> 12
❓ أسئلة شائعة
memcpy.📖 ملخص
- ستة عاملات بت:
&،|،^،~،<<،>> - ضبط بت بـ
|=، وتصفير بـ&= ~، وقلب بـ^=، وفحص بـ& - الأقنعة هي التقنية الأساسية للعمليات على البتات، تُستخدم لاستخراج ودمج حقول البتات
- أعلام الصلاحيات تُدمج بـ OR، وتُفحص بـ AND، وتُصفّر بـ NOT
- الإزاحة لليسار تضرب في 2، والإزاحة لليمين تقسم على 2، والمحاذاة بـ
(n + mask) & ~mask
📝 تمارين
- اكتب دالة تُرجع عدد البتات ذات القيمة 1 في التمثيل الثنائي لعدد صحيح (باستخدام العمليات على البتات، لا القسمة على 2 في حلقة)
- اكتب برنامجًا يستخدم العمليات على البتات لضبط سطوع لون RGB (اضرب كل قناة في معامل وأعد الدمج)
- اكتب دالة تستخرج البتات من m إلى n من عدد صحيح 32 بت (m < n، العد من البت 0)