พอยน์เตอร์ใน Go
Go ยังคงมีพอยน์เตอร์ ซึ่งรับประกันประสิทธิภาพในระดับหนึ่ง และเพื่อ GC ที่ดีขึ้นและความปลอดภัย จึงจำกัดการใช้พอยน์เตอร์
การสร้าง
สำหรับการดำเนินการพอยน์เตอร์มีตัวดำเนินการที่ใช้บ่อยสองตัว หนึ่งคือตัวดำเนินการหาที่อยู่ & อีกตัวคือตัวดำเนินการ dereference * การหาที่อยู่ของตัวแปรหนึ่งจะส่งคืนพอยน์เตอร์ของประเภทที่ตรงกัน เช่น
func main() {
num := 2
p := &num
fmt.Println(p)
}พอยน์เตอร์เก็บที่อยู่ของตัวแปร num
0xc00001c088ตัวดำเนินการ dereference มีสองวัตถุประสงค์ ประการแรกคือการเข้าถึงองค์ประกอบที่พอยน์เตอร์ชี้ไป คือการ dereference เช่น
func main() {
num := 2
p := &num
rawNum := *p
fmt.Println(rawNum)
}p เป็นพอยน์เตอร์ การ dereference พอยน์เตอร์สามารถเข้าถึงองค์ประกอบที่พอยน์เตอร์ชี้ไปได้อีกวัตถุประสงค์หนึ่งคือการประกาศพอยน์เตอร์หนึ่งตัว เช่น
func main() {
var numPtr *int
fmt.Println(numPtr)
}<nil>*int หมายถึงประเภทของตัวแปรนี้เป็นพอยน์เตอร์ประเภท int แต่พอยน์เตอร์ไม่เพียงประกาศเท่านั้น ยังต้องเริ่มต้นด้วย ต้องจัดสรรหน่วยความจำให้มัน มิฉะนั้นจะเป็นพอยน์เตอร์ว่าง ไม่สามารถใช้งานได้ตามปกติ ไม่ก็ใช้ตัวดำเนินการหาที่อยู่เพื่อกำหนดค่าที่อยู่ของตัวแปรอื่นให้กับพอยน์เตอร์นี้ ไม่ก็ใช้ฟังก์ชันในตัว new เพื่อจัดสรรด้วยตนเอง เช่น
func main() {
var numPtr *int
numPtr = new(int)
fmt.Println(numPtr)
}ส่วนใหญ่ใช้ตัวแปรสั้น
func main() {
numPtr := new(int)
fmt.Println(numPtr)
}ฟังก์ชัน new มีพารามิเตอร์เดียวคือประเภท และส่งคืนพอยน์เตอร์ของประเภทที่ตรงกัน ฟังก์ชันจะจัดสรรหน่วยความจำให้พอยน์เตอร์นี้ และพอยน์เตอร์ชี้ไปที่ค่าศูนย์ของประเภทที่ตรงกัน เช่น
func main() {
fmt.Println(*new(string))
fmt.Println(*new(int))
fmt.Println(*new([5]int))
fmt.Println(*new([]float64))
}
0
[0 0 0 0 0]
[]ห้ามการคำนวณพอยน์เตอร์
ใน Go ไม่รองรับการคำนวณพอยน์เตอร์ นั่นคือพอยน์เตอร์ไม่สามารถเลื่อนได้ ดูโค้ด C++ ส่วนนี้ก่อน
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &arr[0];
cout << &arr << endl
<< p << endl
<< p + 1 << endl
<< &arr[1] << endl;
}0x31d99ff880
0x31d99ff880
0x31d99ff884
0x31d99ff884จะเห็นว่าที่อยู่ของอาร์เรย์กับที่อยู่ขององค์ประกอบแรกของอาร์เรย์เหมือนกัน และหลังจากบวกหนึ่งกับการดำเนินการพอยน์เตอร์แล้ว มันชี้ไปที่องค์ประกอบที่สองของอาร์เรย์ ใน Go อาร์เรย์ก็เช่นเดียวกัน แต่ความแตกต่างคือพอยน์เตอร์ไม่สามารถเลื่อนได้ เช่น
func main() {
arr := [5]int{0, 1, 2, 3, 4}
p := &arr
println(&arr[0])
println(p)
// พยายามดำเนินการคำนวณพอยน์เตอร์
p++
fmt.Println(p)
}โปรแกรมแบบนี้จะไม่สามารถผ่านการคอมไพล์ได้ รายงานข้อผิดพลาดดังนี้
main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)TIP
ไลบรารีมาตรฐาน unsafe มีการดำเนินการมากมายสำหรับการเขียนโปรแกรมระดับต่ำ รวมถึงการคำนวณพอยน์เตอร์ ไปที่ ไลบรารีมาตรฐาน-unsafe เพื่อดูรายละเอียด
new และ make
ในหลายบทก่อนหน้าได้กล่าวถึงฟังก์ชันในตัว new และ make หลายครั้งแล้ว ทั้งสองคล้ายกันบ้าง แต่ก็มีความแตกต่างต่างกัน ทบทวนด้านล่าง
func new(Type) *Type- ค่าส่งคืนคือพอยน์เตอร์ของประเภท
- พารามิเตอร์ที่รับคือประเภท
- ใช้เฉพาะสำหรับการจัดสรรหน่วยความจำให้พอยน์เตอร์
func make(t Type, size ...IntegerType) Type- ค่าส่งคืนคือค่า ไม่ใช่พอยน์เตอร์
- พารามิเตอร์ตัวแรกที่รับคือประเภท พารามิเตอร์แบบแปรผันแตกต่างกันไปตามประเภทที่ส่งเข้า
- ใช้เฉพาะสำหรับการจัดสรรหน่วยความจำให้สไลซ์ แมป แชนเนล
ด้านล่างเป็นบางตัวอย่าง
new(int) // พอยน์เตอร์ int
new(string) // พอยน์เตอร์ string
new([]int) // พอยน์เตอร์สไลซ์จำนวนเต็ม
make([]int, 10, 100) // สไลซ์จำนวนเต็มความยาว 10 ความจุ 100
make(map[string]int, 10) // แมปความจุ 10
make(chan int, 10) // แชนเนลขนาดบัฟเฟอร์ 10