AI智能
改变未来

golang学习之第二天


第二天

指针

  • 一个指针变量指向了一个值的内存地址,类似于变量和常量。
  • 指针声明格式如下:
var var_name *var-typestr := new(string)//列var ip *int
  • 指针赋值
var a int= 20   /* 声明实际变量 */var ip *int        /* 声明指针变量 */
  • 获取指针的值指针
/* 使用指针访问值 */fmt.Printf(\"*ip 变量的值: %d\\n\", *ip )
  • Go 空指针
var  ptr *intvar ptr *int;fmt.Println(ptr)fmt.Printf(\"ptr 的值为 : %x\\n\", ptr  )//输出: <nil>//输出: ptr 的值为 : 0
  • 指针数组
package mainimport \"fmt\"const MAX int = 3func main() {a := []int{10,100,200}var i intvar ptr [MAX]*int;for  i = 0; i < MAX; i++ {ptr[i] = &a[i] /* 整数地址赋值给指针数组 */}for  i = 0; i < MAX; i++ {fmt.Printf(\"a[%d] = %d\\n\", i,*ptr[i] )}}
  • 指向指针的指针
package mainimport \"fmt\"func main(){var a int = 5//把ptr指针 指向ss所在地址var ptr *int = &a//开辟一个新的指针,指向ptr指针指向的地方var pts *int = ptr//二级指针,指向一个地址,这个地址存储的是一级指针的地址var pto **int = &ptr//三级指针,指向一个地址,这个地址存储的是二级指针的地址,二级指针同上var pt3 ***int = &ptofmt.Println(\"a的地址:\",&a,\"\\n 值\", a, \"\\n\\n\",\"ptr指针所在地址:\",&ptr,\"\\n ptr指向的地址:\",ptr,\"\\n ptr指针指向地址对应的值\",*ptr,\"\\n\\n\",\"pts指针所在地址:\",&pts,\"\\n pts指向的地址:\", pts,\"\\n pts指针指向地址对应的值:\",*pts,\"\\n\\n\",\"pto指针所在地址:\",&pto,\"\\n pto指向的指针(ptr)的存储地址:\",pto,\"\\n pto指向的指针(ptr)所指向的地址: \" ,*pto,\"\\n pto最终指向的地址对应的值(a)\",**pto,\"\\n\\n\",\"pt3指针所在的地址:\",&pt3,\"\\n pt3指向的指针(pto)的地址:\",pt3,//等于&*pt3,\"\\n pt3指向的指针(pto)所指向的指针的(ptr)地址\", *pt3, //等于&**pt3,\"\\n pt3指向的指针(pto)所指向的指针(ptr)所指向的地址(a):\",**pt3, //等于&***pt3,\"\\n pt3最终指向的地址对应的值(a)\", ***pt3)}//结果/*a的地址: 0xc0000140d8值 5ptr指针所在地址: 0xc00000e028ptr指向的地址: 0xc0000140d8ptr指针指向地址对应的值 5pts指针所在地址: 0xc00000e030pts指向的地址: 0xc0000140d8pts指针指向地址对应的值: 5pto指针所在地址: 0xc00000e038pto指向的指针(ptr)的存储地址: 0xc00000e028pto指向的指针(ptr)所指向的地址:  0xc0000140d8pto最终指向的地址对应的值(a) 5pt3指针所在的地址: 0xc00000e040pt3指向的指针(pto)的地址: 0xc00000e038pt3指向的指针(pto)所指向的指针的(ptr)地址 0xc00000e028pt3指向的指针(pto)所指向的指针(ptr)所指向的地址(a): 0xc0000140d8pt3最终指向的地址对应的值(a) 5*/
  • 总结
  1. 指针是指向值的地址
  2. 指向指针的指针是指向指针的地址

结构体

  • 示例
package mainimport \"fmt\"type Book struct {title stringauthor stringsubject string}func main() {fmt.Println(createBook())//访问结构体成员fmt.Println(createBook().title)}// 创建一个新的结构体func createBook() Book{// book := Book{\"go\", \"ll\", \"编程语言\"}var book Book//成员设置book.title = \"go\"// 忽略的字段为 0 或 空return book}
  • 提示: 定义指向结构体的指针类似于其他指针变量

切片

  • Go 语言切片是对数组的抽象

  • 切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大(可以理解为动态数组)

  • 定义切片(使用make函数)

var slice1 []type = make([]type, len, capacity)//len为切片的初始长度。var slice2 = [] int {1,2,3 } //slice2 后面不能跟类型[]中什么都不写var slice3 =  slice2[0:2] //将slice2中从下标s0到1下的元素截取出来创建为一个新的切片,不设置分别默认0和最后一个
  • 切片函数
len(slice) //获取切片的长度cap(slice) //获取切片的容量slice = append(sliece, value) //向切片中添追加值,可以添加切片copy(slice1,slice2) //拷贝 slice2 的内容到 slices1

语言范围(Range)

  • Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

map集合

  • 定义 Map
/* 声明变量,默认 map 是 nil */var map_variable map[key_data_type]value_data_type/* 使用 make 函数 */map_variable := make(map[key_data_type]value_data_type)
  • 使用示例
package mainimport \"fmt\"func main() {/*创建集合 */// var countryCapitalMap map[string]string  countryCapitalMap = make(map[string]string)/* 创建map */countryCapitalMap := map[string]string{\"France\": \"Paris\", \"Italy\": \"Rome\", \"Japan\": \"Tokyo\", \"India\": \"New delhi\"}fmt.Println(\"原始地图\")/* 打印地图 */for country := range countryCapitalMap {fmt.Println(country, \"首都是\", countryCapitalMap [ country ])}/*删除元素*/delete(countryCapitalMap, \"France\")fmt.Println(\"法国条目被删除\")fmt.Println(\"删除元素后地图\")/*打印地图*/for country := range countryCapitalMap {fmt.Println(country, \"首都是\", countryCapitalMap [ country ])}}
  • map在只读的时候是线程安全的,在写的时候是线程不安全的

  • sync.Map(线程安全的map)

package mainimport (\"fmt\"\"sync\")func main() {var scene sync.Map// 将键值对保存到sync.Mapscene.Store(\"greece\", 97)scene.Store(\"london\", 100)scene.Store(\"egypt\", 200)// 从sync.Map中根据键取值fmt.Println(scene.Load(\"london\"))// 根据键删除对应的键值对scene.Delete(\"london\")// 遍历所有sync.Map中的键值对scene.Range(func(k, v interface{}) bool {fmt.Println(\"iterate:\", k, v)return true})}
  • 列表(List)
var list_nam list.List//第一种定义方式list_name := list.New()//第二种定义方式
  • 列表与切片和 map 不同的是,列表并没有具体元素类型的限制,因此,列表的元素可以是任意类型,这既带来了便利,也引来一些问题,例如给列表中放入了一个 interface{} 类型的值,取出值后,如果要将 interface{} 转换为其他类型将会发生宕机

  • 方法

//均返回操作节点句柄list.InsertAfter(v interface {}, mark * Element) //在 mark 点之后插入元素,mark 点由其他插入函数提供list.InsertBefore(v interface {}, mark * Element) //在 mark 点之前插入元素,mark 点由其他插入函数提供list.PushBackList(other *List)	//添加 other 列表元素到尾部list.PushFrontList(other *List)	//添加 other 列表元素到头部list.Remove(element)
  • 遍历(list是双链表,需要Front() 函数获取头元素,遍历时只要元素不为空就可以继续进行,每一次遍历都会调用元素的 Next() 函数)
l := list.New()// 尾部添加l.PushBack(\"canon\")// 头部添加l.PushFront(67)for i := l.Front(); i != nil; i = i.Next() {fmt.Println(i.Value)}

接口

  • Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
  • 示例
package mainimport \"fmt\"type Phone interface {call()}type NokiaPhone struct {}func (nokiaPhone NokiaPhone) call() {fmt.Println(\"I am Nokia, I can call you!\")}type IPhone struct {}func (iPhone IPhone) call() {fmt.Println(\"I am iPhone, I can call you!\")}func main() {var phone Phonephone = new(NokiaPhone)phone.call()phone = new(IPhone)phone.call()}
  • 注意: 接口中的方法需要被全部实现

并发

  • Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
  • 同一个程序中的所有 goroutine 共享同一个地址空间
  • 开启线程:
go func_name()

通道(channel)

  • 通道声明:
ch := make(chan int)
  • 发送值:
ch <- value
  • 接受值:
value <- ch
  • 注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
  • 示例:
package mainimport \"fmt\"func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 把 sum 发送到通道 c}func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int) //建立通道go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)x, y := <-c, <-c // 从通道 c 中接收fmt.Println(x, y, x+y)}

通道缓冲区

  • 通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
  • 带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

  • 不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了

注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » golang学习之第二天