美文网首页Golang 入门资料+笔记
《GO语言圣经》学习笔记(一)

《GO语言圣经》学习笔记(一)

作者: 半亩房顶 | 来源:发表于2020-07-23 10:08 被阅读0次

    前言

    终于开坑go语言圣经了,本系列笔记中会记录学习《GO语言圣经》中看到的知识点和引申学习。好的,话不多说,开始第一章《入门》

    入门知识点

    Go Range

    底层实现:

    // Arrange to do a loop appropriate for the type.  We will produce
      //   for INIT ; COND ; POST {
      //           ITER_INIT
      //           INDEX = INDEX_TEMP
      //           VALUE = VALUE_TEMP // If there is a value
      //           original statements
      //   }
    
    • 针对数组的编译实现:
    // The loop we generate:
    //   len_temp := len(range)
    //   range_temp := range
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = range_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    
    • 针对切片的编译实现:
    //   for_temp := range
    //   len_temp := len(for_temp)
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = for_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    

    共同的主题是:

    • 所有的一切都只是C风格的循环。
    • 你迭代的东西被赋值给一个临时变量。
    • range表达式只会被赋值一次。(即进入循环前会对切片的长度进行快照,决定一共循环多少次。后面切片的变动不会改变循环次数
    • 补充一点:range以Unicode字符遍历,for循环用utf-8遍历,有中文时,遍历长度不同
    • 针对map的编译实现:
      // The loop we generate:
      //   var hiter map_iteration_struct
      //   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
      //           index_temp = *hiter.key
      //           value_temp = *hiter.val
      //           index = index_temp
      //           value = value_temp
      //           original body
      //   }
    

    遍历map时初始化源码如下

    func mapiterinit(t *maptype, h *hmap, it *hiter) {
        ...
        it.t = t
        it.h = h
        it.B = h.B
        it.buckets = h.buckets
        if t.bucket.kind&kindNoPointers != 0 {
            h.createOverflow()
            it.overflow = h.extra.overflow
            it.oldoverflow = h.extra.oldoverflow
        }
    
        // 重点看这里!!!
        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))
        it.bucket = it.startBucket
        ...
    
        mapiternext(it)
    }
    

    可以注意下,重点关注标注的位置,起始位置是通过fastrand()这个随机函数影响的,所以可想而知,map的遍历从结果上看,是“无序”的

    goroutine

    goroutine是一種函數的併發執行方式,而channel是用來在goroutine之間進行參數傳遞。main函數也是運行在一個goroutine中,而go function則表示創建一個新的goroutine,併在這個這個新的goroutine里執行這個函數。

    指針

    Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據類型。在其它語言中,比如C語言,指針操作是完全不受約束的。在另外一些語言中,指針一般被處理爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。Go語言在這兩種范圍中取了一種平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以獲取指針指向的變量內容,但是在Go語言里沒有指針運算,也就是不能像c語言里可以對指針進行加或減操作

    一個指針的值是另一個變量的地址。一個指針對應變量在內存中的存儲位置。併不是每一個值都會有一個內存地址,但是對於每一個變量必然有對應的內存地址。通過指針,我們可以直接讀或更新對應變量的值,而不需要知道該變量的名字(如果變量有名字的話)。

    引用

    Go Range循环的真相
    为什么遍历 Go map 是无序的?


    欢迎大家关注我的公众号


    半亩房顶

    相关文章

      网友评论

        本文标题:《GO语言圣经》学习笔记(一)

        本文链接:https://www.haomeiwen.com/subject/rwxmhktx.html