Skip to content

Commit

Permalink
update: pointer with more detail and example
Browse files Browse the repository at this point in the history
  • Loading branch information
Ja7ad committed Dec 11, 2023
1 parent 3b4f0d5 commit 3494730
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
Empty file added Untitled.md
Empty file.
139 changes: 138 additions & 1 deletion content/chapter 2/2.1-pointer.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ weight: 3001
var ex *T
```




{{< hint info >}}
در بین برنامه نویسان زبان Go همیشه این مسئله بوده که **کی و کجا** باید از Pointer استفاده کنیم؟!

دیدگاه من نسبت به Pointer :

زمانی باید از Pointer استفاده کنید که **قصد دارید یک متغیری را در scope ها و توابع مختلف مقدار دهی کنید** در اینجا بهتر است از Pointer استفاده کنیم تا جلو کپی شدن متغیر در خانه های مختلف حافظه گرفته شود.

ساده تر بهش بخواهیم نگاه کنیم **وقتی حس کردی میخوای یک متغیر را در چند جای مختلف خارج از اونجایی که تعریف شده مقدار دهی کنی بهتر است آن متغیر را بصورت Pointer برای مقدار دهی پاس دهید.**

حتی این قضیه برای اینکه method تعریف کنیم صدق میکنه که چرا باید متد با Pointer یا بدون Pointer تعریف کنیم.

نکته مهم 1: **استفاده از Pointer باید با دقت انجام شود تا از مشکلاتی مانند دسترسی همزمان به متغیرها و اشتباهات مرتبط با حافظه جلوگیری شود.**

نکته مهم 2: استفاده از Pointer خیلی خوب و مفید است اما در جای درست چون اگر نتوانیم تشخیص دهیم کی و کجا استفاده کنیم به مرور باعث کاهش عملکرد برنامه خواهد شد.

{{< /hint >}}


در مثال بالا ما شیوه تعریف یک متغیر اشاره‌گر را توضیح دادیم. اول کلید واژه ی **var** بعد اسم متغیر و در آخر هم *T یعنی تایپ متغیر. به مثال زیر توجه کنید:


Expand All @@ -27,6 +48,122 @@ var ptr *string
1. استفاده از تابع `new`
2. استفاده از اپراتور `&` (آمپرسند)

### مثال 1

فرض کنید شما 1 متغیر دارید و قصد دارید داخل 3 تابع مختلف مقدارش را بروز کنید و با یک تابع دیگر نمایش دهید:

```go
package main

import "fmt"

func main() {
var count int

addCount(&count)

addCount(&count)

addCountWithoutPointer(count)
fmt.Printf("value = %d, address in memory = %p\n", count, &count)

printCount(count)

}

func addCount(x *int) {
*x++
fmt.Printf("value = %d, address in memory = %p\n", *x, x)
}

func addCountWithoutPointer(x int) {
x++
fmt.Printf("value = %d, address in memory = %p\n", x, &x)
}

func printCount(x int) {
fmt.Printf("value = %d, address in memory = %p\n", x, &x)
}
```

```shell
value = 1, address in memory = 0xc000110068
value = 2, address in memory = 0xc000110068
value = 3, address in memory = 0xc000110088
value = 2, address in memory = 0xc000110068
value = 2, address in memory = 0xc0001100b0
```

![pointer](../../assets/img/content/chapter2/pointer/1.jpg)

در کد فوق ما یک متغیر به نام count ساختیم که داخل تابع (scope) main می باشد.

**رخداد اول:** حال این متغیر را 2 بار بصورت Pointer به تابع **addCount** پاس دادیم و داخل همان تابع مقدار دهیش کردیم و پس از مقدار دهی در همان تابع print ش کردیم.
اتفاقی که افتاد مقدار متغیر در همان خانه حافظه که **0xc0000a6068** هست مقدار دهی شد و عملا بخشی دیگر از حافظه گرفته نشد.

**رخداد دوم:** متغیر را بدون Pointer به تابع **addCountWithoutPointer** پاس دادیم و در همان تابع مقدار دهید و print کردیم,
اتفاقی که افتاد ما متغیر را اینبار بدون Pointer پاس دادیم یعنی عملا یک کپی از متغیر را به تابع **addCountWithoutPointer** فرستادیم و اگر به آدرس حافظه مقدار دقت کنید **0xc0000a6088** عملا یک خانه جدید به این کپی تخصیص داده شد و مقدارش در همان خانه بروز شده و اون متغیر **x** تنها در همان تابع زنده اس و در صورتیکه اگر **x** را از تابع بازگشت دهید دوباره یک کپی از آن به بیرون منتقل می شود.

### مثال 2

فرض کنید یک تایپ count دارید که {{< tooltip text="نام مستعار" note="Alias" >}} تایپ int می باشد و 3 تا متد (متد را در بخش 2.3 می توانید بخوانید) گیرنده Pointer با نام های increase , decrease و print دارند.

```go
package main

import "fmt"

type count int

func main() {
x := new(count)
x.increase()
x.increase()
x.decrease()
x.increase()

x.printWithoutPointer()

}

func (c *count) increase() {
*c++
c.print()
}

func (c *count) decrease() {
*c--
c.print()
}

func (c *count) print() {
fmt.Printf("value = %d, address in memory = %p\n", *c, c)
}

func (c count) printWithoutPointer() {
fmt.Printf("value = %d, address in memory = %p\n", c, &c)
}
```

```shell
value = 1, address in memory = 0xc0000a4068
value = 2, address in memory = 0xc0000a4068
value = 1, address in memory = 0xc0000a4068
value = 2, address in memory = 0xc0000a4068
value = 2, address in memory = 0xc0000a4088
```

ما در مثال فوق با استفاده از تابع new اومدیم متغیر x را ایجاد کردیم سپس متد **increase** برای افزایش مقدار متغیر x و متد **decrease** را برای کاهش مقدار x و در نهایت print را برای چاپ استفاده کردیم.

در اینجا به دلیل گیرنده Pointer بودن تایپ **count** توانستیم درهمان خانه حافظه مقدار x را افزایش یا کاهش دهیم و در نهایت با استفاده از متد print اومدیم مقدار و خانه حافظه را چاپ کردیم.

اما یک متد **printWithoutPointer** داریم که یک کپی از مقدار **x** را چاپ میکند و عملا مقدار را از یک خانه حافظه جدید را به نمایش میگذارد.

{{< hint info >}}
متد **printWithoutPointer** بدون Pointer می باشد و زمانیکه سایر متدهایتان با یا بدون Pointer هست بهتر است متدهای جدیدتان با Pointer باشد تا جلو سردرگمی گرفته شود. طبق داکیومنت های ارائه شده برای Go چندان لزومی ندارد چنین ترکیبی انجام دهید.

{{< /hint >}}

## 2.1.1 استفاده از تابع new

یک {{< tooltip text="اشاره‌گر" note="pointer" >}} با استفاده از تابع `new` بصورت مثال زیر تعریف شده است:
Expand Down Expand Up @@ -133,7 +270,7 @@ b := &a
c := &b
```

![array](../../assets/img/content/chapter2/pointer/1.jpg)
![array](../../assets/img/content/chapter2/pointer/2.jpg)

همانطور که در مثال و عکس بالا می‌بینید، متغیر a مقدارش ۲ و آدرسش در حافظه `0xXXXXXX` است. در مقدار متغیر b ما اشاره کردیم به آدرس حافظه متغیر a و در ادامه در متغیر c به آدرس حافظه متغیر b اشاره کردیم.

Expand Down
Binary file modified static/assets/img/content/chapter2/pointer/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/assets/img/content/chapter2/pointer/2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3494730

Please sign in to comment.