Setiap bilangan bulat di dalam memori hanyalah rangkaian bit bernilai 0 dan 1. Ketika program perlu bekerja pada level tersebut — misalnya menyimpan banyak penanda dalam satu bilangan atau memproses data biner — kita memakai operator bitwise. Go menyediakan AND, OR, XOR, AND NOT, serta pergeseran bit ke kiri dan ke kanan. Pada chapter ini kita mencoba setiap operator dengan visualisasi biner, membaca tabel kebenaran per bit, lalu menerapkan pola yang paling sering dipakai di dunia nyata: mask permission.

Daftar Operator Bitwise #

Tabel berikut merangkum operator bitwise pada Go. Seluruh operator hanya bekerja pada tipe bilangan bulat, bukan pada float maupun bool:

OperatorNamaContohHasil
&AND0b1010 & 0b01100b0010
|OR0b1010 | 0b01100b1110
^XOR (biner)0b1010 ^ 0b01100b1100
^NOT (unary)^c, c uint8 = 0b0000_10100b1111_0101
&^AND NOT0b1010 &^ 0b01100b1000
<<Geser kiri1 << 38
>>Geser kanan16 >> 24

Operator ^ mempunyai dua makna tergantung posisi. Di antara dua operand, ^ adalah XOR: hasilnya 1 hanya jika kedua bit berbeda. Di depan satu operand, ^ adalah NOT yang membalik seluruh bit. Operator &^ membersihkan bit: setiap bit 1 pada operand kanan dipaksa menjadi 0 pada hasil.

Tabel Kebenaran per Bit #

Karena operator bitwise memproses setiap bit secara mandiri, tabel kebenaran satu bit berikut merangkum tiga operator utama:

aba & ba | ba ^ b
00000
01011
10011
11110

Program berikut menerapkan tabel tersebut pada dua bilangan empat bit, lalu mencoba unary NOT dan pergeseran bit. Literal 0b1010 adalah notasi biner; Go juga mendukung notasi heksadesimal seperti 0xF:

go
package main

import "fmt"

func main() {
	a := 0b1010 // desimal 10
	b := 0b110  // desimal 6

	fmt.Printf("a      = %04b (%d)\n", a, a)
	fmt.Printf("b      = %04b (%d)\n", b, b)
	fmt.Printf("a & b  = %04b (%d)\n", a&b, a&b)
	fmt.Printf("a | b  = %04b (%d)\n", a|b, a|b)
	fmt.Printf("a ^ b  = %04b (%d)\n", a^b, a^b)
	fmt.Printf("a &^ b = %04b (%d)\n", a&^b, a&^b)

	// ^ sebagai operator unary: membalik semua bit (NOT)
	var c uint8 = 0b0000_1010
	fmt.Printf("^c     = %08b (%d)\n", ^c, ^c)

	// Pergeseran bit ke kiri dan ke kanan
	fmt.Printf("1 << 3  = %d\n", 1<<3)
	fmt.Printf("16 >> 2 = %d\n", 16>>2)

	// Jumlah pergeseran tidak dibatasi lebar tipe
	var kecil uint8 = 1
	var geser uint = 9
	fmt.Println("kecil << geser =", kecil<<geser)
}

Output yang Dihasilkan:

text
a      = 1010 (10)
b      = 0110 (6)
a & b  = 0010 (2)
a | b  = 1110 (14)
a ^ b  = 1100 (12)
a &^ b = 1000 (8)
^c     = 11110101 (245)
1 << 3  = 8
16 >> 2 = 4
kecil << geser = 0

Mari kita telaah baris demi baris program di atas:

  1. Baris 6-7 (a := 0b1010 dan b := 0b110): Kedua variable memakai literal biner: 0b1010 adalah 10 desimal dan 0b110 adalah 6 desimal. Notasi biner memudahkan kita membaca bitnya.
  2. Baris 11 (a & b): AND membandingkan kedua bilangan bit demi bit; hanya bit yang 1 di kedua tempat tetap 1, menghasilkan 0010.
  3. Baris 12-13 (a | b dan a ^ b): OR menghasilkan 1110 karena bit 1 dari operand mana pun ikut terbawa. XOR menghasilkan 1100; hanya bit yang berbeda antara kedua operand yang bernilai 1.
  4. Baris 14 (a &^ b): AND NOT menghapus bit 0110 dari 1010 dan menyisakan 1000 — gunakan operator ini untuk mematikan penanda.
  5. Baris 17-18 (var c uint8 ... ^c): Unary NOT membalik kedelapan bit c: 0000_1010 menjadi 1111_0101 (245). Tipe uint8 dipakai agar mudah dibaca; pada int8, bit paling kiri adalah penanda tanda.
  6. Baris 21-22 (1 << 3 dan 16 >> 2): Geser kiri mengalikan dengan 2 sebanyak tiga kali (1 menjadi 8); geser kanan membagi dengan 2 sebanyak dua kali (16 menjadi 4).
  7. Baris 25-27 (kecil << geser): Jumlah pergeseran tidak dibatasi lebar tipe: menggeser uint8 sebanyak 9 kali mendorong seluruh bit keluar, sehingga hasilnya 0.

Mask Permission: Praktik Nyata Bitwise #

Pola paling populer operator bitwise adalah bit mask: beberapa penanda disimpan dalam satu bilangan, satu bit untuk setiap penanda. Ketiga konstanta berikut dideklarasikan seperti pada chapter Konstanta :

go
package main

import "fmt"

const (
	baca     = 1 << 0 // 001 = 1
	jalankan = 1 << 1 // 010 = 2
	tulis    = 1 << 2 // 100 = 4
)

func main() {
	// OR menggabungkan beberapa permission menjadi satu mask
	admin := baca | jalankan | tulis
	tamu := baca

	fmt.Printf("admin = %03b (%d)\n", admin, admin)
	fmt.Printf("tamu  = %03b (%d)\n", tamu, tamu)

	// AND menguji apakah satu permission aktif
	fmt.Println("admin bisa tulis   :", admin&tulis != 0)
	fmt.Println("tamu bisa tulis    :", tamu&tulis != 0)

	// AND NOT mencabut permission dari mask
	editor := admin &^ jalankan
	fmt.Printf("editor = %03b (%d)\n", editor, editor)
	fmt.Println("editor bisa jalankan:", editor&jalankan != 0)
}

Output yang Dihasilkan:

text
admin = 111 (7)
tamu  = 001 (1)
admin bisa tulis   : true
tamu bisa tulis    : false
editor = 101 (5)
editor bisa jalankan: false

Mari kita telaah bagian-bagian penting program di atas:

  1. Baris 6-8 (const ( baca = 1 << 0 ... )): Ketiga konstanta adalah pergeseran dari angka 1 sehingga setiap penanda menempati bit yang berbeda; 1 << 2 sekaligus mendokumentasikan posisinya.
  2. Baris 13 (admin := baca | jalankan | tulis): OR menggabungkan tiga penanda menjadi mask 111 (7) — satu bilangan kecil menyimpan tiga permission.
  3. Baris 20-21 (admin&tulis != 0): AND menguji satu bit: hasilnya tidak nol hanya jika bit tulis aktif. Operator & mengikat lebih kuat daripada !=; tanda kurung (admin & tulis) != 0 juga sah untuk kejelasan.
  4. Baris 24 (editor := admin &^ jalankan): AND NOT mencabut satu penanda tanpa mengganggu bit lain: 111 menjadi 101 (5). Pola | untuk memasang dan &^ untuk mencabut dipakai luas pada konfigurasi sistem operasi dan package flag.

Referensi #

Bekerja pada level bit sering diikuti operasi “ubah sedikit, simpan kembali” — misalnya menyalakan satu bit lalu menyimpannya pada variable yang sama. Chapter Assignment Operator memperkenalkan bentuk ringkas |= dan &^= untuk pola tersebut.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

bitwise-demo.go

Unduh berkas
go
package main

import "fmt"

func main() {
	a := 0b1010 // desimal 10
	b := 0b110  // desimal 6

	fmt.Printf("a      = %04b (%d)\n", a, a)
	fmt.Printf("b      = %04b (%d)\n", b, b)
	fmt.Printf("a & b  = %04b (%d)\n", a&b, a&b)
	fmt.Printf("a | b  = %04b (%d)\n", a|b, a|b)
	fmt.Printf("a ^ b  = %04b (%d)\n", a^b, a^b)
	fmt.Printf("a &^ b = %04b (%d)\n", a&^b, a&^b)

	// ^ sebagai operator unary: membalik semua bit (NOT)
	var c uint8 = 0b0000_1010
	fmt.Printf("^c     = %08b (%d)\n", ^c, ^c)

	// Pergeseran bit ke kiri dan ke kanan
	fmt.Printf("1 << 3  = %d\n", 1<<3)
	fmt.Printf("16 >> 2 = %d\n", 16>>2)

	// Jumlah pergeseran tidak dibatasi lebar tipe
	var kecil uint8 = 1
	var geser uint = 9
	fmt.Println("kecil << geser =", kecil<<geser)
}

perizinan.go

Unduh berkas
go
package main

import "fmt"

const (
	baca     = 1 << 0 // 001 = 1
	jalankan = 1 << 1 // 010 = 2
	tulis    = 1 << 2 // 100 = 4
)

func main() {
	// OR menggabungkan beberapa permission menjadi satu mask
	admin := baca | jalankan | tulis
	tamu := baca

	fmt.Printf("admin = %03b (%d)\n", admin, admin)
	fmt.Printf("tamu  = %03b (%d)\n", tamu, tamu)

	// AND menguji apakah satu permission aktif
	fmt.Println("admin bisa tulis   :", admin&tulis != 0)
	fmt.Println("tamu bisa tulis    :", tamu&tulis != 0)

	// AND NOT mencabut permission dari mask
	editor := admin &^ jalankan
	fmt.Printf("editor = %03b (%d)\n", editor, editor)
	fmt.Println("editor bisa jalankan:", editor&jalankan != 0)
}