Golang指针

2018-01-27 03:45:01

golang中的指针与c语言指针略有不同。

1、基本类型的指针

var num int = 10
var p *int = &num   // 指针的类型为所指向的变量的类型前加*

2、数组的指针与指针数组

数组的指针:

arr := [3]int{1, 2, 3}
var arrp *[3]int  // 数组的指针
a := &arr   // 直接取数组变量的地址赋值会报错(arrp = &arr)
arrp = a   // 通过第三个变量传递

指针数组:

arr := [3]*int{}
numArr := [3]int{1, 2, 3}
for i, v := range numArr {
    arr[i] = &v
    fmt.Println(*arr[i])
}

3、字典的指针

maps := map[string]string{
    "name" : "ludis",
    "age": "18",
}
var p *map[string]string = &maps // 字典可直接取地址赋值给指针变量,不同于数组,需要通过中间变量
fmt.Println((*p)["name"])   // 取值时*指针变量需要括号括起来

4、函数的指针与指针函数

函数的指针:

package main

import "fmt"

func main {
    fmt.Println(add)  // 0x1093c40
}

func add() {}
  • 函数声明后,函数名变量本身就是指针类型,存储的是指向函数体的地址。
  • 因此当对函数变量赋值给其他变量时,属于浅拷贝,只是拷贝指向函数的地址。

指针函数:指针函数与结构体方法类似,见下。

5、 结构体的指针

结构体创建对象:

package main

import "fmt"

type Person struct {
    name string
    age string
}

func main() {
    // var关键字声明对象
    var person1 = Person{
        "佩奇",
        "3",
    } 
    // := 简写声明
    person2 := Person{
        "佩奇粑粑",
        "28",
    }
    // new关键字声明
    var person3 = new(Person) // 等同于 person3 := &Person{"佩奇麻麻", "26"}
    person3.name = "佩奇麻麻" 
    person3.age = "26"
    
    fmt.Println(person1, person2, person3) // {佩奇 3} {佩奇粑粑 28} &{佩奇麻麻 26}
    
    p1 := person1
    p2 := person2
    p3 := person3
    p1.name = "猪宝宝"
    p1.name = "猪爹"
    p1.name = "猪妈"
    fmt.Println(person1, person2, person3) // {佩奇 3} {佩奇粑粑 28} &{猪妈 26}
}

从结果可以看出:

  • 前两种声明方式创建,变量中直接存储的是对象,对其进行拷贝为深拷贝。
  • 使用new创建,变量中存储的是地址,对其拷贝为浅拷贝。

结构体的方法:

package main

import "fmt"

type Person struct {
    name string
    age int
}

func (p Person) setName() {
    p.name = "小猪佩奇"
}

func (p *Person) setAge(age int) {
    p.age = age
}

func main() {
    // var关键字声明对象, 等同于:=声明
    var person = Person{"佩奇", 3} 
    fmt.Println(person.name, person.age) // 佩奇 3
    person.setName()
    person.setAge(6)
    fmt.Println(person.name, person.age) // 佩奇 6
    // new 声明
    pigone :=  new(Person) // 🌚 not pgone
    pigone.name = "pig"
    pigone.age = 69
    fmt.Println(pigone) // &{pig 69}
    pigone.setName()
    pigone.setAge(96)
    fmt.Println(pigone) // &{pig 96}

}

由结果可知:

  • 结构体的方法,如果传入的值为对象类型时(setName声明方式),不管传入的是值类型(对象)还是引用类型(指针),在方法中都会深拷贝传入的对象,修改值后传入的对象不收影响。
  • 如果结构体方法传传参类型为指针类型时(setAge声明方式),不管传入的是什么类型,都属于浅拷贝,修改值影响传入的对象。

6、interface 接口

package main

import "fmt"

// 结构体
type Person struct {
    name string
    age int
}

// 结构体方法
func (p Person) fun1 {
    p.name = "大猪"
}

func (p *Person) fun2 {
    p.name = "小猪"
}

// 接口
type PersonInfo interface {
    fun1()
    fun2()
}

func main() {
    person := Person{"猪猪侠", 9}
    person1 := new(Person)
    var p PersonInfo = person // 报错
    var p PersonInfo = &person // 正确
    var p PersonInfo = person1 // 正确
}

结论:

  • 声明接口时,接口中的函数必须在结构体中有声明
  • 当结构体的方法中,有一个(最少一个)是引用类型(传递指针)的方法时,那么给接口变量赋值时,必须使用引用类型!

END🤔

Golang并发编程、串行编程

线程概要 1、什么是进程,线程,多线程? 开个QQ,开了一个进程;开了迅雷,开了一个进程。 在QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。 所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作支撑的完成QQ的运行,那么这“多个工作”分别有一个线程。 所以一个进程管着多个线程。 通俗的讲:“进程是爹妈,管着众多的线程儿子”... 作者:南理汉子 链接:https://www.zhihu.com/question/25532384/answer/131906596 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 进程: 一个正在运行的程序一般就是一个进程,一个进程中可以有多个线程 线程:一条有序的CPU命令的集合体 多线程:多条有序CPU命令的集合体 备注:一个CPU在同一时刻只能执行一个CPU命令

Golang channel

1、什么是 channel(管道/通道)? 在 golang 中,channel 是多个 goroutine (线程) 之间传递和同步数据的一种手段。同一时刻,仅有一个线程可以向 channel 发送数据,同样的,同一时刻也只能有一个线程能从 channel 读取数据。 channel 的特性使得它可以解决并发编程可能造成的死锁问题,同时也是 Mutex (上锁)及 sync 多线程编程的替代方案,可以简化代码结构。 2、channel 的声明方式 var c chan int // var 关键字声明,缓冲int类型数据 var c chan string // 缓冲string类型数据 c := make(chan int) // make关键字声明,