
تفاوت Pointer در Go و Reference در زبانهای دیگر؛ هرآنچه باید بدانید
تفاوت Pointer در Go و Reference در زبانهای دیگر
اگر تازه وارد دنیای Go شده باشید، احتمالاً بعد از دیدن علامت * و & کمی گیج شدهاید.
شاید هم قبلاً با زبانهایی مثل Java، Python یا C# کار کرده باشید و با خودتان گفته باشید:
مگر این زبانها هم Reference ندارند؟ پس چرا Go دوباره Pointer معرفی کرده است؟
این سؤال کاملاً طبیعی است.
در واقع یکی از مهمترین تفاوتهای Go با بسیاری از زبانهای مدرن، نحوه مدیریت حافظه است.
در این مقاله قصد داریم به صورت کاملاً ساده و عملی یاد بگیریم:
- Pointer چیست؟
- Reference چیست؟
- تفاوت این دو چیست؟
- چرا Go از Pointer استفاده میکند؟
- چرا Go مثل C خطرناک نیست؟
- در چه مواقعی باید از Pointer استفاده کنیم؟
در پایان این مقاله دیگر دیدن *person یا &value برایتان ترسناک نخواهد بود.
Pointer چیست؟
کامپیوتر تمام اطلاعات را داخل حافظه (RAM) ذخیره میکند.
فرض کنید این متغیر را داریم:
x := 10
در ظاهر فقط عدد ۱۰ را ذخیره کردهایم.
اما در واقع اتفاق دیگری افتاده است.
کامپیوتر جایی در حافظه را انتخاب کرده و عدد ۱۰ را داخل آن قرار داده است.
اینجا:
- مقدار برابر 10 است.
- آدرس حافظه برابر 1000 است.
البته این آدرس فقط برای مثال است.
در دنیای واقعی آدرسها چیزی شبیه این هستند:
0xc000012090
Pointer دقیقا چیست؟
Pointer یعنی: متغیری که به جای نگهداری مقدار، آدرس آن مقدار را نگهداری میکند.
مثلاً:
x := 10 p := &x
حالا:
x = 10 p = آدرس x
اگر چاپ کنیم:
fmt.Println(x)
خروجی:
10
اگر چاپ کنیم:
fmt.Println(&x)
خروجی چیزی شبیه:
0xc000012090
و اگر چاپ کنیم:
fmt.Println(p)
دقیقاً همان آدرس را خواهیم دید.
علامت & در Go چیست؟
علامت & یعنی:
آدرس این متغیر را به من بده.
مثال:
package main
import "fmt"
func main() {
age := 30
fmt.Println(&age)
}خروجی:
0xc000012090
البته آدرس شما متفاوت خواهد بود.
علامت * در Go چیست؟
علامت * دو کاربرد دارد.
۱- تعریف Pointer
var p *int
یعنی:
p یک Pointer به int است.
۲- دسترسی به مقدار
فرض کنید:
x := 50 p := &x
اگر بنویسیم:
fmt.Println(*p)
خروجی:
50
یعنی:
از روی آدرس برو مقدار واقعی را بخوان.
یک مثال واقعی
package main
import "fmt"
func main() {
x := 20
p := &x
fmt.Println(x)
fmt.Println(p)
fmt.Println(*p)
}خروجی تقریباً:
20 0xc000012090 20
اولی مقدار است.
دومی آدرس است.
سومی دوباره مقدار واقعی است.
تغییر مقدار از طریق Pointer
جذابترین بخش Pointer همین است.
package main
import "fmt"
func main() {
x := 10
p := &x
*p = 100
fmt.Println(x)
}100
ما مستقیماً متغیر x را تغییر ندادیم.
بلکه از طریق آدرس حافظه مقدار آن را عوض کردیم.
تصویر ذهنی Pointer
فرض کنید خانهای دارید.
خانه:
آدرس: خیابان آزادی پلاک ۲۰
داخل خانه:
تلویزیون
اگر کسی فقط آدرس خانه را داشته باشد، میتواند وارد خانه شود و تلویزیون را عوض کند.
Pointer دقیقاً همین کار را انجام میدهد.
Pointer خودِ داده نیست.
بلکه آدرس داده است.
چرا اصلاً Pointer ساخته شد؟
فرض کنید یک Struct بسیار بزرگ دارید.
type User struct {
Name string
Email string
Address string
Phone string
...
}اگر هر بار این Struct را به یک تابع ارسال کنیم، کل اطلاعات کپی میشود.
اگر این Struct چندین کیلوبایت حجم داشته باشد، برنامه به مرور کندتر خواهد شد.
اما اگر فقط Pointer ارسال کنیم:
func UpdateUser(u *User)
به جای کپی کردن کل Struct، فقط یک آدرس حافظه (که معمولاً ۸ بایت روی سیستمهای ۶۴ بیتی است) ارسال میشود.
نتیجه:
- مصرف حافظه کمتر
- سرعت بیشتر
- کپی کمتر
- عملکرد بهتر
در بخش دوم وارد تفاوتهای عمیق Pointer در Go با Reference در Java، Python، C#، C++ و Rust میشویم، همچنین توضیح میدهم چرا Pointerهای Go بسیار ایمنتر از Pointerهای زبان C هستند و با مثالهای متعدد، رایجترین اشتباهات برنامهنویسان را بررسی میکنیم.




