第二天
指针
一个指针变量指向了一个值的内存地址,类似于变量和常量。指针声明格式如下:
var var_name
*var_type
str
:= new(string)
var ip
*int
指针赋值
var a
int= 20
var ip
*int
获取指针的值指针
fmt
.Printf("*ip 变量的值: %d\n", *ip
)
Go 空指针
var ptr
*int
var ptr
*int;
fmt
.Println(ptr
)
fmt
.Printf("ptr 的值为 : %x\n", ptr
)
指针数组
package main
import "fmt"
const MAX
int = 3
func main() {
a
:= []int{10,100,200}
var i
int
var 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 main
import "fmt"
func main(){
var a
int = 5
var ptr
*int = &a
var pts
*int = ptr
var pto
**int = &ptr
var pt3
***int = &pto
fmt
.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
,
"\n pt3指向的指针(pto)所指向的指针的(ptr)地址", *pt3
,
"\n pt3指向的指针(pto)所指向的指针(ptr)所指向的地址(a):",**pt3
,
"\n pt3最终指向的地址对应的值(a)", ***pt3
)
}
总结
指针是指向值的地址指向指针的指针是指向指针的地址
结构体
示例
package main
import "fmt"
type Book
struct {
title
string
author
string
subject
string
}
func main() {
fmt
.Println(createBook())
fmt
.Println(createBook().title
)
}
func createBook() Book
{
var book Book
book
.title
= "go"
return book
}
提示: 定义指向结构体的指针类似于其他指针变量
切片
Go 语言切片是对数组的抽象
切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大(可以理解为动态数组)
定义切片(使用make函数)
var slice1
[]type = make([]type, len, capacity
)
var slice2
= [] int {1,2,3 }
var slice3
= slice2
[0:2]
切片函数
len(slice
)
cap(slice
)
slice
= append(sliece
, value
)
copy(slice1
,slice2
)
语言范围(Range)
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
map集合
定义 Map
var map_variable
map[key_data_type
]value_data_type
map_variable
:= make(map[key_data_type
]value_data_type
)
使用示例
package main
import "fmt"
func main() {
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 main
import (
"fmt"
"sync"
)
func main() {
var scene sync
.Map
scene
.Store("greece", 97)
scene
.Store("london", 100)
scene
.Store("egypt", 200)
fmt
.Println(scene
.Load("london"))
scene
.Delete("london")
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
)
list
.InsertBefore(v
interface {}, mark
* Element
)
list
.PushBackList(other
*List
)
list
.PushFrontList(other
*List
)
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 main
import "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 Phone
phone
= new(NokiaPhone
)
phone
.call()
phone
= new(IPhone
)
phone
.call()
}
注意: 接口中的方法需要被全部实现
并发
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。同一个程序中的所有 goroutine 共享同一个地址空间开启线程:
go func_name()
通道(channel)
通道声明:
ch
:= make(chan int)
发送值:
ch
<- value
接受值:
value
<- ch
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。示例:
package main
import "fmt"
func sum(s
[]int, c
chan int) {
sum
:= 0
for _, v
:= range s
{
sum
+= v
}
c
<- sum
}
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
fmt
.Println(x
, y
, x
+y
)
}
通道缓冲区
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch
:= make(chan int, 100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。