GoCasts آموزش Go به زبان ساده

بیش از ۱۰۰۰ شرکت‌کننده یادگیری Go و Backend رو از امروز شروع کن
ثبت‌نام دوره + تیم‌سازی

آموزش اشاره‌گر در گولنگ - راهنمای کامل Pointer در Go

اشاره‌گر (Pointer) متغیری است که آدرس حافظه یک متغیر دیگر را ذخیره می‌کند. درک pointer‌ها برای نوشتن کد کارآمد در Go ضروری است.

چرا Pointer؟

  1. کارایی: به جای کپی کردن داده بزرگ، فقط آدرس را پاس می‌دهیم
  2. تغییر مقدار: امکان تغییر متغیر اصلی در تابع
  3. اشتراک داده: چند بخش برنامه به یک داده دسترسی دارند

سینتکس پایه

package main

import "fmt"

func main() {
    x := 42

    // دریافت آدرس با &
    ptr := &x

    fmt.Println("مقدار x:", x)          // 42
    fmt.Println("آدرس x:", &x)          // 0xc0000140a0
    fmt.Println("مقدار ptr:", ptr)      // 0xc0000140a0
    fmt.Println("مقدار در آدرس:", *ptr) // 42 (dereference)
}

عملگرها

عملگر نام کاربرد
& Address-of دریافت آدرس متغیر
* Dereference دسترسی به مقدار در آدرس

تعریف Pointer

package main

import "fmt"

func main() {
    // روش 1: تعریف و مقداردهی
    var x int = 10
    var ptr *int = &x

    // روش 2: استنتاج نوع
    y := 20
    ptr2 := &y

    // روش 3: pointer به struct
    type Person struct {
        Name string
        Age  int
    }
    p := Person{Name: "علی", Age: 25}
    pPtr := &p

    fmt.Printf("ptr: %T, value: %v\n", ptr, *ptr)
    fmt.Printf("ptr2: %T, value: %v\n", ptr2, *ptr2)
    fmt.Printf("pPtr: %T, name: %v\n", pPtr, pPtr.Name)
}

nil Pointer

Pointer بدون مقداردهی nil است:

var ptr *int  // nil

if ptr == nil {
    fmt.Println("Pointer خالی است")
}

// دسترسی به nil pointer باعث panic می‌شود!
// fmt.Println(*ptr)  // panic: runtime error

تغییر مقدار با Pointer

package main

import "fmt"

func main() {
    x := 10
    ptr := &x

    // تغییر مقدار از طریق pointer
    *ptr = 20

    fmt.Println("x:", x)  // 20 (تغییر کرد!)
}

Pointer در توابع

بدون Pointer (کپی مقدار)

func double(x int) {
    x = x * 2  // فقط کپی تغییر می‌کند
}

func main() {
    num := 5
    double(num)
    fmt.Println(num)  // 5 (تغییر نکرد!)
}

با Pointer (تغییر مقدار اصلی)

func double(x *int) {
    *x = *x * 2  // مقدار اصلی تغییر می‌کند
}

func main() {
    num := 5
    double(&num)
    fmt.Println(num)  // 10 (تغییر کرد!)
}

برگرداندن Pointer

func newInt(x int) *int {
    return &x  // در Go این امن است (escape analysis)
}

func main() {
    ptr := newInt(42)
    fmt.Println(*ptr)  // 42
}

Pointer به Struct

package main

import "fmt"

type User struct {
    Name  string
    Email string
    Age   int
}

// با pointer - می‌تواند تغییر دهد
func (u *User) SetAge(age int) {
    u.Age = age
}

// بدون pointer - فقط خواندن
func (u User) GetInfo() string {
    return fmt.Sprintf("%s (%d)", u.Name, u.Age)
}

func main() {
    user := User{Name: "علی", Email: "ali@example.com", Age: 25}

    // Go خودکار & و * را مدیریت می‌کند
    user.SetAge(30)  // معادل (&user).SetAge(30)
    fmt.Println(user.GetInfo())  // علی (30)

    // کار با pointer
    userPtr := &user
    userPtr.Name = "رضا"  // معادل (*userPtr).Name
    fmt.Println(user.Name)  // رضا
}

new و make

new - تخصیص حافظه و برگرداندن pointer

// new یک pointer به مقدار صفر برمی‌گرداند
ptr := new(int)      // *int با مقدار 0
fmt.Println(*ptr)    // 0

*ptr = 42
fmt.Println(*ptr)    // 42

// معادل:
var x int
ptr2 := &x

make - برای slice، map، channel

// make مقدار واقعی برمی‌گرداند (نه pointer)
slice := make([]int, 5)     // []int
m := make(map[string]int)   // map[string]int
ch := make(chan int)        // chan int

Pointer به Pointer

x := 10
ptr := &x
ptrToPtr := &ptr

fmt.Println(x)          // 10
fmt.Println(*ptr)       // 10
fmt.Println(**ptrToPtr) // 10

**ptrToPtr = 20
fmt.Println(x)          // 20

Pointer در Slice و Map

Slice (Reference Type)

func modifySlice(s []int) {
    s[0] = 100  // تغییر می‌دهد (slice reference است)
}

func main() {
    nums := []int{1, 2, 3}
    modifySlice(nums)
    fmt.Println(nums)  // [100 2 3]
}

Map (Reference Type)

func addToMap(m map[string]int) {
    m["new"] = 42  // تغییر می‌دهد
}

func main() {
    data := map[string]int{"a": 1}
    addToMap(data)
    fmt.Println(data)  // map[a:1 new:42]
}

مثال‌های کاربردی

Swap با Pointer

func swap(a, b *int) {
    *a, *b = *b, *a
}

func main() {
    x, y := 10, 20
    swap(&x, &y)
    fmt.Println(x, y)  // 20 10
}

Linked List

type Node struct {
    Value int
    Next  *Node
}

func main() {
    // ایجاد لیست: 1 -> 2 -> 3
    head := &Node{Value: 1}
    head.Next = &Node{Value: 2}
    head.Next.Next = &Node{Value: 3}

    // پیمایش
    current := head
    for current != nil {
        fmt.Println(current.Value)
        current = current.Next
    }
}

Optional Parameters با Pointer

type Config struct {
    Host    string
    Port    *int    // اختیاری
    Timeout *int    // اختیاری
}

func NewConfig(host string, port, timeout *int) Config {
    return Config{
        Host:    host,
        Port:    port,
        Timeout: timeout,
    }
}

func main() {
    port := 8080
    config := NewConfig("localhost", &port, nil)

    if config.Port != nil {
        fmt.Println("Port:", *config.Port)
    }
    if config.Timeout == nil {
        fmt.Println("Timeout: default")
    }
}

Value Receiver vs Pointer Receiver

type Counter struct {
    count int
}

// Value receiver - کپی می‌شود
func (c Counter) ValueIncrement() {
    c.count++  // فقط کپی تغییر می‌کند
}

// Pointer receiver - اصل تغییر می‌کند
func (c *Counter) PointerIncrement() {
    c.count++
}

func main() {
    counter := Counter{count: 0}

    counter.ValueIncrement()
    fmt.Println(counter.count)  // 0

    counter.PointerIncrement()
    fmt.Println(counter.count)  // 1
}

کی از کدام استفاده کنیم؟

استفاده از Pointer Receiver استفاده از Value Receiver
وقتی می‌خواهید تغییر دهید وقتی فقط می‌خوانید
struct بزرگ است struct کوچک است
consistency با متدهای دیگر نوع‌های پایه مثل int

نکات مهم

۱. Go آدرس stack را مدیریت می‌کند

// این در Go امن است (بر خلاف C)
func createInt() *int {
    x := 42
    return &x  // Go به heap منتقل می‌کند
}

۲. Pointer arithmetic نداریم

// این در Go کار نمی‌کند:
// ptr++
// ptr + 1

۳. همیشه nil را بررسی کنید

func process(ptr *int) {
    if ptr == nil {
        return
    }
    // کار با *ptr
}

جدول خلاصه

عملیات سینتکس نتیجه
دریافت آدرس &x *int
دریافت مقدار *ptr int
تعریف pointer var p *int nil pointer
تخصیص با new new(int) *int با مقدار 0

قدم‌های بعدی

منابع

بیش از ۱۰۰۰ شرکت‌کننده یادگیری Go و Backend رو از امروز شروع کن
ثبت‌نام دوره + تیم‌سازی