ข้ามไปยังเนื้อหา

Pointers (ตัวชี้)

ใน TypeScript (และ JavaScript) object ถูกส่งต่อเป็น reference โดยอัตโนมัติ คุณไม่เคยต้องเขียนว่า “ขอ address ของตัวแปรนี้” เพราะ runtime จัดการ memory ให้เบื้องหลังโดยที่คุณมองไม่เห็น

Go เปิดเผย memory address อย่างชัดเจน คุณเลือกเองว่า function จะรับ copy ของค่าหรือ pointer ไปยังต้นฉบับ การเลือกนั้นมีผลจริงต่อความถูกต้องและประสิทธิภาพ

&x ให้ pointer ไปยัง x — address ใน memory ที่ x อาศัยอยู่ *p dereference pointer p — อ่านหรือเขียนค่าที่ address นั้น

TypeScript
// TypeScript — object ถูกแชร์แบบ implicit
function increment(obj: { count: number }) {
obj.count++; // mutate ต้นฉบับ (reference)
}
const counter = { count: 0 };
increment(counter);
console.log(counter.count); // 1
// แต่ primitive ถูก copy เสมอ:
function addOne(n: number): number {
return n + 1; // n คือ copy — caller ไม่เปลี่ยน
}
let x = 5;
addOne(x);
console.log(x); // ยังเป็น 5
Go
// Go — primitive ถูก copy โดยค่าเริ่มต้น; ใช้ * เพื่อ opt in การ mutate
func increment(n *int) {
*n++ // dereference แล้ว mutate
}
func main() {
x := 42
increment(&x) // ส่ง address ของ x
fmt.Println(x) // 43 — x ถูก mutate
// ถ้าไม่ใช้ pointer — value จะถูก copy, caller ไม่เปลี่ยน
// increment(x) จะเป็น compile error (type ไม่ตรง)
}

ทุก type T มี pointer type *T ที่สอดคล้องกัน ตัวแปร pointer เก็บ address ไม่ใช่ค่า

TypeScript
// TypeScript ไม่มี pointer type
// ที่ใกล้ที่สุดคือ wrapper object หรือ ref pattern:
const ref = { current: 0 }; // "pointer" แบบ manual
Go
var p *int // p คือ pointer ไปยัง int, ค่าคือ nil
x := 10
p = &x // p เก็บ address ของ x
fmt.Println(*p) // 10 — dereference เพื่ออ่าน
*p = 20 // เขียนผ่าน pointer
fmt.Println(x) // 20

มีสองเหตุผลหลักในการใช้ pointer:

  1. Mutation — คุณต้องการให้ function แก้ไขตัวแปรของ caller (ไม่ใช่ทำงานกับ copy)
  2. Efficiency — struct มีขนาดใหญ่และการ copy ทุกครั้งที่เรียกจะสิ้นเปลือง

สำหรับ struct ขนาดเล็ก (2–3 field) ที่อ่านอย่างเดียว มักใช้ value semantics ได้ สำหรับ struct ขนาดใหญ่หรือจุดที่ต้องการ mutation ให้ใช้ pointer

TypeScript
// TypeScript — struct/object ส่งเป็น reference เสมอ
// คุณไม่มีทางเลือก; runtime ตัดสินใจ
interface Config { host: string; port: number; }
function connect(cfg: Config) { /* cfg คือ reference */ }
Go
type Config struct {
Host string
Port int
}
// Value receiver — method รับ copy; ไม่สามารถแก้ไขต้นฉบับ
func (c Config) String() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
// Pointer receiver — method สามารถแก้ไขต้นฉบับ
func (c *Config) SetPort(p int) {
c.Port = p
}
func main() {
cfg := Config{Host: "localhost", Port: 8080}
cfg.SetPort(9090)
fmt.Println(cfg.String()) // localhost:9090
}
package main
import "fmt"
func increment(n *int) {
*n++
}
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
x := 42
fmt.Println("before:", x)
increment(&x)
fmt.Println("after increment:", x)
a, b := 10, 20
fmt.Printf("before swap: a=%d b=%d\n", a, b)
swap(&a, &b)
fmt.Printf("after swap: a=%d b=%d\n", a, b)
// Pointer ไปยัง struct
type Point struct{ X, Y int }
p := &Point{X: 1, Y: 2}
p.X = 99 // Go auto-dereference: (*p).X = 99
fmt.Println("point:", *p)
}
&x ใน Go หมายความว่าอะไร?
*p หมายความว่าอะไรเมื่อ p เป็น pointer?
Zero value ของตัวแปร pointer ใน Go คืออะไร?
คุณมี func double(n int) จะทำอย่างไรให้ double สามารถแก้ไขตัวแปรของ caller ได้?