Operator bitwise adalah alat untuk bekerja langsung di level bit. Setiap nilai di C disimpan sebagai deretan bit di memori, dan operator bitwise memungkinkan kita memanipulasi bit-bit tersebut satu per satu. Pada Chapter ini kita akan menelaah enam operator bitwise di C: & (AND), | (OR), ^ (XOR), ~ (NOT), << (geser kiri), dan >> (geser kanan). Operator-operator ini adalah pondasi dari pemrograman low-level seperti penulisan device driver, manipulasi hardware register, kompresi data, kriptografi, hingga optimasi performa pada embedded systems. Kita juga akan menelaah beberapa idiom klasik: pemeriksaan ganjil/genap dengan & 1, manipulasi bitmask dengan shift, hingga trik swap variabel tanpa variabel sementara menggunakan XOR.

Enam Operator Bitwise #

OperatorNamaContoh a=0xCA, b=0xB5Hasil
&AND bitwisea & b0x80
|OR bitwisea | b0xFF
^XOR bitwisea ^ b0x7F
~NOT bitwise (unary)~abit dibalik
<<Shift kiria << 20x328
>>Shift kanana >> 20x32

Operator bitwise bekerja pada representasi biner nilai integer, bit demi bit. Hasil setiap operator mengikuti tabel kebenaran untuk bit tunggal: 1 & 1 = 1, 1 | 0 = 1, 1 ^ 1 = 0, ~1 = 0, dan seterusnya.

c
#include <stdio.h>

void cetak_biner(unsigned int nilai, int lebar) {
    for (int i = lebar - 1; i >= 0; i--) {
        putchar((nilai >> i) & 1 ? '1' : '0');
    }
}

int main(void) {
    unsigned int a = 0b11001010;  /* 202 */
    unsigned int b = 0b10110101;  /* 181 */

    printf("a      = "); cetak_biner(a, 8); printf("  (0x%X)\n", a);
    printf("b      = "); cetak_biner(b, 8); printf("  (0x%X)\n", b);
    printf("a & b  = "); cetak_biner(a & b, 8); printf("  (0x%X)\n", a & b);
    printf("a | b  = "); cetak_biner(a | b, 8); printf("  (0x%X)\n", a | b);
    printf("a ^ b  = "); cetak_biner(a ^ b, 8); printf("  (0x%X)\n", a ^ b);
    printf("~a     = "); cetak_biner(~a, 8); printf("  (0x%X)\n", ~a);
    printf("a << 2 = "); cetak_biner(a << 2, 8); printf("  (0x%X)\n", a << 2);
    printf("a >> 2 = "); cetak_biner(a >> 2, 8); printf("  (0x%X)\n", a >> 2);
    return 0;
}

Output yang Dihasilkan:

text
a      = 11001010  (0xCA)
b      = 10110101  (0xB5)
a & b  = 10000000  (0x80)
a | b  = 11111111  (0xFF)
a ^ b  = 01111111  (0x7F)
~a     = 00110101  (0xFFFFFF35)
a << 2 = 00101000  (0x328)
a >> 2 = 00110010  (0x32)

Mari kita telaah baris-baris kode di atas:

  1. Baris 4-8 (cetak_biner): fungsi cetak biner 8 bit dengan loop dari bit ke-7 ke bit ke-0; ekspresi (nilai >> i) & 1 menggeser nilai sehingga bit ke-i menjadi bit paling rendah, lalu di-AND dengan 1 untuk mengisolasi bit tunggal tersebut.
  2. Baris ke-17 (a & b): AND bitwise—hanya bit yang 1 di kedua operand yang tetap 1; hasil 0b10000000.
  3. Baris ke-19 (a ^ b): XOR bitwise—bit 1 jika dan hanya jika kedua operand berbeda; hasil 0b01111111.
  4. Baris ke-20 (~a): NOT unary—membalik setiap bit; namun karena a bertipe unsigned int (32 bit), hasilnya dilengkapi bit 1 di 24 bit atas sehingga nilai heksadesimalnya 0xFFFFFF35. Fungsi cetak_biner hanya menampilkan 8 bit paling rendah.
  5. Baris ke-21 (a << 2): shift kiri 2 posisi—setara dengan perkalian a * 4; bit-bit baru yang masuk dari kanan selalu 0.

Idiom Manipulasi Bitmask #

Pola paling umum di pemrograman low-level adalah memeriksa, menyetel, menghapus, dan men-toggle bit ke-n pada sebuah register atau flag. Empat operasi ini memiliki idiom baku yang berpusat pada ekspresi 1U << n yang menghasilkan bitmask dengan hanya satu bit bernilai 1 di posisi ke-n.

c
#include <stdio.h>

/* Cek apakah bit ke-n diset */
int cek_bit(unsigned int nilai, int n) {
    return (nilai >> n) & 1U;
}

/* Set bit ke-n */
unsigned int set_bit(unsigned int nilai, int n) {
    return nilai | (1U << n);
}

/* Clear bit ke-n */
unsigned int clear_bit(unsigned int nilai, int n) {
    return nilai & ~(1U << n);
}

/* Toggle bit ke-n */
unsigned int toggle_bit(unsigned int nilai, int n) {
    return nilai ^ (1U << n);
}

int main(void) {
    unsigned int data = 0b00001010;   /* 10 */
    printf("data awal       : 0b00001010 = %u\n", data);
    printf("bit ke-1 diset? : %d\n", cek_bit(data, 1));
    printf("bit ke-3 diset? : %d\n", cek_bit(data, 3));

    unsigned int hasil = set_bit(data, 0);
    printf("set_bit(0)      : %u\n", hasil);

    hasil = clear_bit(data, 3);
    printf("clear_bit(3)    : %u\n", hasil);

    hasil = toggle_bit(data, 1);
    printf("toggle_bit(1)   : %u\n", hasil);
    return 0;
}

Output yang Dihasilkan:

text
data awal       : 0b00001010 = 10
bit ke-1 diset? : 1
bit ke-3 diset? : 1
set_bit(0)      : 11
clear_bit(3)    : 2
toggle_bit(1)   : 8

Pada baris ke-28, cek_bit(data, 1) menggeser data ke kanan satu posisi sehingga bit ke-1 menjadi bit ke-0, lalu di-AND dengan 1U. Hasilnya 1 yang berarti bit ke-1 memang diset. Pada baris ke-31, set_bit(data, 0) meng-OR data dengan 0b00000001 untuk memastikan bit ke-0 menjadi 1 tanpa mengubah bit lain; 10 | 1 = 11.

Idiom Lain: Ganjil/Genap, Popcount, dan Swap dengan XOR #

Operator bitwise memungkinkan banyak trik ringkas. Pemeriksaan ganjil/genap dapat dilakukan dengan n & 1 alih-alih n % 2. Operasi popcount (menghitung jumlah bit bernilai 1) dapat dilakukan dengan loop shift. Bahkan swap dua variabel dapat dilakukan tanpa variabel sementara menggunakan tiga operasi XOR berturut-turut.

c
#include <stdio.h>

/* Cek ganjil/genap dengan & 1 */
const char *ganjil_genap(int n) {
    return (n & 1) ? "ganjil" : "genap";
}

/* Hitung bit '1' (popcount) */
int hitung_bit(unsigned int n) {
    int count = 0;
    while (n) {
        count += n & 1;
        n >>= 1;
    }
    return count;
}

int main(void) {
    int angka[] = {7, 10, 15, 16, 255};
    int jumlah = sizeof(angka) / sizeof(angka[0]);

    for (int i = 0; i < jumlah; i++) {
        printf("%3d adalah %s, jumlah bit 1 = %d\n",
               angka[i],
               ganjil_genap(angka[i]),
               hitung_bit((unsigned int)angka[i]));
    }

    /* Swap tanpa variabel sementara menggunakan XOR */
    int a = 25, b = 17;
    printf("\nSebelum swap: a=%d, b=%d\n", a, b);
    a ^= b;
    b ^= a;
    a ^= b;
    printf("Setelah swap: a=%d, b=%d\n", a, b);
    return 0;
}

Output yang Dihasilkan:

text
  7 adalah ganjil, jumlah bit 1 = 3
 10 adalah genap, jumlah bit 1 = 2
 15 adalah ganjil, jumlah bit 1 = 4
 16 adalah genap, jumlah bit 1 = 1
255 adalah ganjil, jumlah bit 1 = 8

Sebelum swap: a=25, b=17
Setelah swap: a=17, b=25

Pada baris ke-25, n & 1 mengisolasi bit ke-0; jika 1, maka n ganjil. Pada baris ke-41, swap XOR bekerja karena a ^ a = 0 dan a ^ 0 = a. Setelah a ^= b, variabel a menyimpan gabungan XOR keduanya; operasi b ^= a mengembalikan nilai asli a ke b; operasi a ^= b mengembalikan nilai asli b ke a.

Operator bitwise membuka pintu ke pemrograman level rendah. Pada Chapter berikutnya, kita akan melihat operator conditional ? : yang memungkinkan percabangan ringkas dalam satu ekspresi.