非淡泊无以明志,非宁静无以致远。这篇文章主要讲述Go语言学习查缺补漏ing Day7相关的知识,希望能为你提供帮助。
Go语言学习查缺补漏ing Day7
零、前言因为笔者基础不牢,在使用Go语言的时候经常遇到很多摸不着头脑的问题,所以笔者下定决心好好对Go语言进行查漏补缺,本【Go语言查缺补漏ing】系列主要是帮助新手Gopher更好的了解Go语言的易错点、重难点。希望各位看官能够喜欢,点点赞、关注一下呗!
一、再谈defer的执行顺序大家来看一看这段代码:
package mainimport "fmt"type Person struct {
age int
}
func main() {
person := &
Person{28}//A
defer func(p *Person) {
fmt.Println(p.age)
}(person)
//B
defer fmt.Println(person.age)
//C
defer func() {
fmt.Println(person.age)
}()
person.age = 21
}
前面我们介绍过defer的执行顺序,但是我今天又遇到新问题,于是这里又补充介绍这个defer的顺序问题。
这个程序运行结果是:
21
28
21
【Go语言学习查缺补漏ing Day7】我们都知道defer的执行顺序是先进后出,所以执行顺序是C、B、A。
B中
defer fmt.Println(person.age)
输出28,为什么呢?因为这里是将28作为defer()函数的参数,会把28推入栈中进行缓存,得到执行这条defer语句时就把它拿出来。所以输出28.
而A中:
defer func(p *Person) {
fmt.Println(p.age)
}(person)
defer()函数是将结构体Person的地址进行缓存,当后续改变这个地址的内值时,后续输出时这里就会输出那个地址内改变后的值。所以B defer语句执行时从地址中取出的值是29.
而C defer语句理由很简单:
defer func() {
fmt.Println(person.age)
}()
就是无参匿名函数的一种情形。闭包引用,person.age改变就会改变。
二、哪种切片的声明比较好?为什么?
var a []int
a := []int{}
这里第一种声明的是nil切片,而第二种声明是创建一个长度以及容量为零的空切片。
第一种切片声明方法比较好,因为它这种声明方式不占用空间,而第二种声明后会占用一部分空间。
三、取得结构体成员的几种方法
package mainimport "fmt"type S struct {
m string
}func f() *S {
return &
S{"ReganYue"}
}
func main() {
p := f()
p2 := *f()
fmt.Println(p.m, p2.m)
}
我们运行能够发现:
p、p2都能获取结构体的成员变量。
*为什么呢?f()函数的返回值是指针类型,所以p2获取f()时,p2是S类型,p2.m可以获取其成员变量。**
而f()的结果是指针,不过我们前面说过,一级指针能够自动进行解引用。所以也能够访问成员变量。
四、遍历map的存在顺序变化?为什么?我们执行下面这段代码多次,看输出结果:
package mainimport "fmt"func main() {
m := map[int]string{0: "zero", 1: "one", 3: "three", 4: "four", 5: "five"}
for k, v := range m {
fmt.Println(k, v)
}
}
第一次执行结果如下:
5 five
0 zero
1 one
3 three
4 four
第二次执行结果如下:
0 zero
1 one
3 three
4 four
5 five
第三次执行结果如下:
4 four
5 five
0 zero
1 one
3 three
我们发现每一次执行的顺序都是变化的。这说明遍历map的顺序是无序的。为什么呢?
在runtime.mapiterinit中有这样一段代码:
// mapiterinit initializes the hiter struct used for ranging over maps.
// The hiter struct pointed to by it is allocated on the stack
// by the compilers order pass or on the heap by reflect_mapiterinit.
// Both need to have zeroed hiter since the struct contains pointers.
func mapiterinit(t *maptype, h *hmap, it *hiter) {
if raceenabled &
&
h != nil {
callerpc := getcallerpc()
racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiterinit))
}if h == nil || h.count == 0 {
return
}if unsafe.Sizeof(hiter{})/sys.PtrSize != 12 {
throw("hash_iter size incorrect") // see cmd/compile/internal/gc/reflect.go
}
it.t = t
it.h = h// grab snapshot of bucket state
it.B = h.B
it.buckets = h.buckets
if t.bucket.ptrdata =https://www.songbingjia.com/android/= 0 {
// Allocate the current slice and remember pointers to both current and old.
// This preserves all relevant overflow buckets alive even if
// the table grows and/or overflow buckets are added to the table
// while we are iterating.
h.createOverflow()
it.overflow = h.extra.overflow
it.oldoverflow = h.extra.oldoverflow
}// decide where to start
r := uintptr(fastrand())
if h.B >
31-bucketCntBits {
r += uintptr(fastrand()) <
<
31
}
it.startBucket = r &
bucketMask(h.B)
it.offset = uint8(r >
>
h.B &
(bucketCnt - 1))// iterator state
it.bucket = it.startBucket// Remember we have an iterator.
// Can run concurrently with another mapiterinit().
if old := h.flags;
old&
(iterator|oldIterator) != iterator|oldIterator {
atomic.Or8(&
h.flags, iterator|oldIterator)
}mapiternext(it)
}
// decide where to start
r := uintptr(fastrand())
if h.B >
31-bucketCntBits {
r += uintptr(fastrand()) <
<
31
}
it.startBucket = r &
bucketMask(h.B)
it.offset = uint8(r >
>
h.B &
(bucketCnt - 1))// iterator state
it.bucket = it.startBucket
我们可以看到,决定从哪开始是根据fastrand()取随机数决定的,所以每次运行,随机数都不一样,所以输出顺序也不一样。
推荐阅读
- #yyds干货盘点# 解决(standard_init_linux.go:219: exec user process caused)
- 在页面中插入代码样式时,响应式仅处于活动状态
- 调整WordPress标头中的图标大小
- 调整标题图片仅适用于手机吗()
- 使用URL调整WordPress中图像的大小
- 从WordPress中删除列的填充
- WordPress主题中的重置/刷新搜索表单按钮
- 在WordPress管理员中替换user-edit.php页面
- 在免费的wordpress主题中对section进行重新排序