指针传递调用函数
对于基本类型的指针,用法和C一样
func func1(x *int) {
(*x) += 1
}
func main() {
var x = 1
var p *int = &x
fmt.Println(*p)
func1(p)
fmt.Println(*p)
}
func func1(x []int) {
x[0] += 1
}
func func2(x []int) {
x = append(x, 5)
}
func main() {
var x []int = []int{1, 2}
fmt.Println(x) [1 2]
func1(x)
fmt.Println(x) //[2 2]
func2(x)
fmt.Println(x) //[2 2]
}
func func2(x *[]int) {
*x = append(*x, 5)
}
func main() {
x := []int{}
func2(&x)
fmt.Println(x) //[5]
}
引用类型和结构体类型的赋值
注意对于切片或数组类型,直接截取后给新变量赋值后,修改新变量会影响旧变量
func main() {
var x []int = []int{1, 2, 3, 4, 5}
fmt.Printf("%T, %v\n", x, x) //[]int, [1 2 3 4 5]
y := x[0:]
fmt.Printf("%T, %v\n", y, y) //[]int, [1 2 3 4 5]
y[0] += 1
fmt.Printf("%T, %v\n", x, x) //[]int, [2 2 3 4 5]
fmt.Printf("%T, %v\n", y, y) //[]int, [2 2 3 4 5],会修改原来的切片的值
}
func main() {
var x [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Printf("%T, %v\n", x, x) //[5]int, [1 2 3 4 5]
y := x[0:]
fmt.Printf("%T, %v\n", y, y) //[]int, [1 2 3 4 5]
y[0] += 1
fmt.Printf("%T, %v\n", x, x) //[5]int, [2 2 3 4 5]
fmt.Printf("%T, %v\n", y, y) //[]int, [2 2 3 4 5],会修改原来的数组的值
}
对于结构体,属于值类型;除非传递的是结构体的地址引用,否则修改新变量不会对就变量产生副作用(但是有浅拷贝的影响)
package main
import "fmt"
type Person struct {
Id int
Ptr *int
}
// 总的来说两个不同的变量一定占用不同的内存空间,关键是变量存的值是多少
func main() {
x := 1
p := Person{Id: 0, Ptr: &x} //这种方式相当于直接复制了一个结构体
fmt.Printf("%T, %v, %p, %p, %d\n", p, p, &(p.Id), p.Ptr, *p.Ptr) //两个不同的变量一定占用不同的内存空间,关键是变量存的值是多少
pp := p //浅拷贝, Id和Ptr都重新占用了新的内存,但是Ptr的值还是指向原来的位置
fmt.Printf("%T, %v, %p, %p, %d\n", pp, pp, &(pp.Id), pp.Ptr, *pp.Ptr) //main.Person, {0 0xc00000a0d8}, 0xc0000260a0, 0xc00000a0d8, 1
pp.Id = 1
(*pp.Ptr) = 2
fmt.Printf("%T, %v, %p, %p, %d\n", p, p, &(p.Id), p.Ptr, *p.Ptr) //main.Person, {0 0xc00000a0d8}, 0xc000026070, 0xc00000a0d8, 2
fmt.Printf("%T, %v, %p, %p, %d\n", pp, pp, &(pp.Id), pp.Ptr, *pp.Ptr) //main.Person, {1 0xc00000a0d8}, 0xc0000260a0, 0xc00000a0d8, 2
fmt.Printf("---\n")
y := 1
p2 := &Person{Id: 0, Ptr: &y} //p2是一个地址,直接把地址的值赋给了pp2
fmt.Printf("%T, %v, %p, %p, %d\n", p2, p2, &(p2.Id), p2.Ptr, *p2.Ptr) //*main.Person, &{0 0xc00000a118}, 0xc000026110, 0xc00000a118, 1
pp2 := p2
fmt.Printf("%T, %v, %p, %p, %d\n", pp2, pp2, &(pp2.Id), pp2.Ptr, *pp2.Ptr) //*main.Person, &{0 0xc00000a118}, 0xc000026110, 0xc00000a118, 1
pp2.Id = 1
(*pp2.Ptr) = 2
fmt.Printf("%T, %v, %p, %p, %d\n", p2, p2, &(p2.Id), p2.Ptr, *p2.Ptr) //*main.Person, &{0 0xc00000a118}, 0xc000026110, 0xc00000a118, 2
fmt.Printf("%T, %v, %p, %p, %d\n", pp2, pp2, &(pp2.Id), pp2.Ptr, *pp2.Ptr) //*main.Person, &{0 0xc00000a118}, 0xc000026110, 0xc00000a118, 2
fmt.Printf("---\n")
z := 1
p3 := new(Person) //p3是一个地址,直接把地址赋值给了pp3,这种方式等价于p2:=&Person{Id: 0, Ptr: &y}
p3.Id, p3.Ptr = 0, &z
fmt.Printf("%T, %v, %p, %p, %d\n", p3, p3, &(p3.Id), p3.Ptr, *p3.Ptr)
pp3 := p3
fmt.Printf("%T, %v, %p, %p, %d\n", pp3, pp3, &(pp3.Id), pp3.Ptr, *pp3.Ptr)
pp3.Id = 1
(*pp3.Ptr) = 2
fmt.Printf("%T, %v, %p, %p, %d\n", p3, p3, &(p3.Id), p3.Ptr, *p3.Ptr)
fmt.Printf("%T, %v, %p, %p, %d\n", pp3, pp3, &(pp3.Id), pp3.Ptr, *pp3.Ptr)
/*
*main.Person, &{0 0xc0000960d8}, 0xc00008a120, 0xc0000960d8, 1
*main.Person, &{0 0xc0000960d8}, 0xc00008a120, 0xc0000960d8, 1
*main.Person, &{1 0xc0000960d8}, 0xc00008a120, 0xc0000960d8, 2
*main.Person, &{1 0xc0000960d8}, 0xc00008a120, 0xc0000960d8, 2
*/
fmt.Printf("---\n")
}
make和new的区别
new主要用于基本数据类型和自定义结构体类型(切片等类型当然也可以,但是不常用),申请一块内存空间,然后返回指向这快内存的指针
make只用于切片、映射和通道等,返回初始化后的变量本身,因为这三个类型是引用类型
func main() {
arr := new([]int)
(*arr) = append((*arr), 1)
fmt.Println(arr, *arr) //&[1] [1]
}
func main() {
p := new(int) //new返回的是指针
fmt.Printf("%v, %v\n", p, *p) //0xc00000a0d8, 0
p1 := []int{1, 2}
fmt.Printf("%T, %v\n", p1, p1) //[]int, [1 2]
p3 := make([]int, 5)
fmt.Printf("%T, %v\n", p3, p3) //[]int, [0 0 0 0 0]
p2 := [5]int{}
fmt.Printf("%T, %v\n", p2, p2) //[5]int, [0 0 0 0 0]
}
结构体和指针
先说结论:
- 结构体的方法和普通函数修改指针的效果是一样的,没有区别
- UpdatePerson1和UpdatePerson2是等价的,go语言语法糖将
p.Id转换成了(*p).Id
package main
import "fmt"
type Person struct {
Id int
}
func UpdatePerson1(p *Person) {
p.Id = 1
}
func UpdatePerson2(p *Person) {
(*p).Id = 2
}
func UpdatePerson3(p Person) {
//按值传递,可类比C语言的按值传递
p.Id = 3
}
func (p *Person) UpdatePerson4() {
p.Id = 4
}
func (p *Person) UpdatePerson5() {
(*p).Id = 5
}
func (p Person) UpdatePerson6() {
p.Id = 6
}
func main() {
p := Person{Id: 0}
fmt.Printf("%T, %v\n", p, p) //main.Person, {0}
UpdatePerson1(&p)
fmt.Printf("%T, %v\n", p, p) //main.Person, {1}
UpdatePerson2(&p)
fmt.Printf("%T, %v\n", p, p) //main.Person, {2}
UpdatePerson3(p)
fmt.Printf("%T, %v\n", p, p) //main.Person, {2}
fmt.Printf("---\n")
p2 := &Person{Id: 0}
fmt.Printf("%T, %v\n", p2, p2) //*main.Person, &{0}
UpdatePerson1(p2) //有作用
fmt.Printf("%T, %v\n", p2, p2) //*main.Person, &{1}
UpdatePerson2(p2) //有作用
fmt.Printf("%T, %v\n", p2, p2) //*main.Person, &{2}
UpdatePerson3(*p2) //没有作用
fmt.Printf("%T, %v\n", p2, p2) //*main.Person, &{2}
fmt.Printf("---\n")
p3 := new(Person)
fmt.Printf("%T, %v\n", p3, p3) //*main.Person, &{0}
fmt.Println(p3) //&{0}
UpdatePerson1(p3)
fmt.Printf("%T, %v\n", p3, p3) //*main.Person, &{1}
UpdatePerson2(p3)
fmt.Printf("%T, %v\n", p3, p3) //*main.Person, &{2}
UpdatePerson3(*p3)
fmt.Printf("%T, %v\n", p3, p3) //*main.Person, &{2}
fmt.Printf("---\n")
p4 := Person{Id: 0}
fmt.Printf("%T, %v\n", p4, p4) //main.Person, {0}
p4.UpdatePerson4()
fmt.Printf("%T, %v\n", p4, p4) //main.Person, {4}
p4.UpdatePerson5()
fmt.Printf("%T, %v\n", p4, p4) //main.Person, {5}
p4.UpdatePerson6()
fmt.Printf("%T, %v\n", p4, p4) //main.Person, {5}
fmt.Printf("---\n")
p5 := &Person{Id: 0}
fmt.Printf("%T, %v\n", p5, p5) //*main.Person, &{0}
p5.UpdatePerson4() //有作用
fmt.Printf("%T, %v\n", p5, p5) //*main.Person, &{4}
p5.UpdatePerson5() //有作用
fmt.Printf("%T, %v\n", p5, p5) //*main.Person, &{5}
p5.UpdatePerson6() //没有作用
fmt.Printf("%T, %v\n", p5, p5) //*main.Person, &{5}
fmt.Printf("---\n")
p6 := new(Person)
fmt.Printf("%T, %v\n", p6, p6) //*main.Person, &{0}
fmt.Println(p6) //&{0}
p6.UpdatePerson4()
fmt.Printf("%T, %v\n", p6, p6) //*main.Person, &{4}
p6.UpdatePerson5()
fmt.Printf("%T, %v\n", p6, p6) //*main.Person, &{5}
p6.UpdatePerson6()
fmt.Printf("%T, %v\n", p6, p6) //*main.Person, &{5}
fmt.Printf("---\n")
}
C语言通过指针访问结构体成员示例
#include <stdio.h> #include <stdlib.h> struct Person{ int id; }; void UpdatePerson(Person *p){ p->id += 1; } void UpdatePerson2(Person *p){ (*p).id +=1; } int main(){ Person p; p.id = 1; UpdatePerson(&p); printf("%d\n", p.id); //2 UpdatePerson2(&p); printf("%d\n", p.id); //3 return 0; }
另外,按地址传递调用函数,以下三种方式定义的变量等价,都会修改原始变量的值,关键是函数的参数是按地址传递
type Person struct {
Id int
}
func UpdatePerson1(p *Person) {
fmt.Printf("[UpdatePerson1] &p: %p\n", p)
p.Id = 1
}
func main() {
p := Person{Id: 0} //这种方式传入的p变量的地址,函数内部和调用的变量是相同的内存空间
fmt.Printf("%T, %p, %v\n", p, &p, p) //main.Person, 0xc000096068, {0}
UpdatePerson1(&p) //[UpdatePerson1] &p: 0xc000096068
fmt.Printf("%T, %p, %v\n", p, &p, p) //[UpdatePerson1] &p: 0xc000096068
fmt.Printf("---\n")
p2 := &Person{Id: 0} ////这种方式传入的p变量的地址,函数内部和调用的变量是相同的内存空间
fmt.Printf("%T, %p, %v\n", p2, p2, p2) //*main.Person, 0xc00000a118, &{0}
UpdatePerson1(p2) //[UpdatePerson1] &p: 0xc00000a118
fmt.Printf("%T, %p, %v\n", p2, p2, p2) //*main.Person, 0xc00000a118, &{1}
fmt.Printf("---\n")
p3 := new(Person)
fmt.Printf("%T, %p, %v\n", p3, p3, p3)
UpdatePerson1(p3)
fmt.Printf("%T, %p, %v\n", p3, p3, p3)
fmt.Printf("---\n")
}
~
map
map底层是一个hash表,通过键值对进行映射