最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語(yǔ)言必知的5個(gè)核心知識(shí)點(diǎn)之init、路徑、輸出、切片、Map

 更新時(shí)間:2026年05月26日 11:22:48   作者:stark張宇  
數(shù)組和切片是?Go?語(yǔ)言中常見(jiàn)的數(shù)據(jù)結(jié)構(gòu),很多剛剛使用?Go?的開(kāi)發(fā)者往往會(huì)混淆這兩個(gè)概念,當(dāng)然還有其他的一些知識(shí)點(diǎn),這篇文章主要介紹了Go語(yǔ)言必知的5個(gè)核心知識(shí)點(diǎn)之init、路徑、輸出、切片、Map的相關(guān)資料,需要的朋友可以參考下

init 函數(shù)

Go 中的 init() 函數(shù)是特殊的初始化函數(shù),用于包級(jí)別的初始化操作,它的執(zhí)行時(shí)機(jī)有嚴(yán)格的規(guī)則,是 Go 程序啟動(dòng)流程中固定的一環(huán)。在當(dāng)前包被導(dǎo)入 / 程序啟動(dòng)時(shí),init() 函數(shù)會(huì)在 main 函數(shù)執(zhí)行之前,自動(dòng)被 Go 運(yùn)行時(shí)調(diào)用執(zhí)行,無(wú)需手動(dòng)調(diào)用。

執(zhí)行順序(優(yōu)先級(jí)從高到低)

Go 程序的初始化執(zhí)行流程是固定的:

1、單個(gè)文件/包內(nèi)規(guī)則

  • 一個(gè)源文件中可以寫(xiě)多個(gè) init() 函數(shù),按代碼順序從上到下的順序執(zhí)行
  • 一個(gè)下有多個(gè)文件,每個(gè)文件的 init() 都會(huì)執(zhí)行,執(zhí)行順序由編譯器的文件編譯順序決定
  • init() 函數(shù)無(wú)參數(shù)、無(wú)返回值,不能被手動(dòng)調(diào)用

2、多個(gè)文件

  • 如果程序?qū)肓硕鄠€(gè)包,且不相互依賴(lài),按照import順序執(zhí)行
  • 不同package且相互依賴(lài),最后被依賴(lài)的最先被執(zhí)行

示例:main 包導(dǎo)入 pkgApkgA 導(dǎo)入 pkgB,執(zhí)行順序從最底層的pkgB開(kāi)始執(zhí)行

Go語(yǔ)言執(zhí)行順序

一、全局順序

1、初始化被依賴(lài)的包(遞歸執(zhí)行,深度優(yōu)先)

2、初始化當(dāng)前包(包級(jí)別 常量,包級(jí)別 變量(按聲明順序),執(zhí)行包內(nèi)的 init() 函數(shù))

3、最后執(zhí)行主文件中的main()函數(shù)

二、單個(gè) Go 文件內(nèi)部執(zhí)行順序(最常用)

同一個(gè) .go 文件里,執(zhí)行順序嚴(yán)格如下:

1、import 導(dǎo)入(只觸發(fā)依賴(lài)包初始化)

2、包級(jí)常量初始化

3、包級(jí)變量初始化

4、文件內(nèi)的 init 函數(shù)(從上往下)

5、main 函數(shù)(如果是 main 包)

三、總結(jié)中的關(guān)鍵點(diǎn)

1、init 函數(shù)自動(dòng)執(zhí)行,不能手動(dòng)調(diào)用

2、一個(gè)文件可以寫(xiě)多個(gè) init,按從上到下執(zhí)行

3、一個(gè)包無(wú)論被導(dǎo)入多少次,init 只執(zhí)行一次

4、初始化順序:常量 → 變量 → init → main

5、多包依賴(lài):深度優(yōu)先,被依賴(lài)包先初始化

項(xiàng)目根目錄

在GO語(yǔ)言開(kāi)發(fā)中,在 go run、編譯二進(jìn)制、docker、不同工作目錄、不同系統(tǒng)下,是 5 個(gè)不同的路徑,所以讀取項(xiàng)目路徑顯得尤為重要:

os.Executable

最穩(wěn)定、最推薦的方法,不管是 go run 還是運(yùn)行編譯后的二進(jìn)制文件,都能拿到正確路徑。

package main

import (
  "fmt"
  "os"
  "path/filepath"
)

// GetProjectRoot 獲取項(xiàng)目根目錄
func GetProjectRoot() (string, error) {
  // 獲取當(dāng)前可執(zhí)行文件的絕對(duì)路徑
  exePath, err := os.Executable()
  if err != nil {
    return "", err
  }

  // 獲取可執(zhí)行文件所在目錄
  exeDir := filepath.Dir(exePath)

  // 如果是 go run 模式,exe 在臨時(shí)目錄,需要向上找 go.mod
  if isGoRun() {
    return findGoModDir(exeDir)
  }
  return exeDir, nil
}

// 判斷是否是 go run 運(yùn)行
func isGoRun() bool {
  return filepath.Base(os.Args[0]) == "go" ||
    filepath.Base(os.Args[0]) == "go.exe" ||
    filepath.HasPrefix(os.Args[0], os.TempDir())
}

// 向上查找 go.mod 所在目錄 = 項(xiàng)目根目錄
func findGoModDir(startDir string) (string, error) {
  dir := startDir
  for {
    modPath := filepath.Join(dir, "go.mod")
    if _, err := os.Stat(modPath); err == nil {
      return dir, nil
    }

    parent := filepath.Dir(dir)
    // 已經(jīng)到根目錄還沒(méi)找到,退出
    if parent == dir {
      break
    }
    dir = parent
  }

  return "", fmt.Errorf("未找到 go.mod,不是 Go 項(xiàng)目")
}

func main() {
  root, err := GetProjectRoot()
  if err != nil {
    panic(err)
  }
  fmt.Println("項(xiàng)目根目錄:", root)
}

slices包

Go 1.21 提供了 slices 包,也可以用更簡(jiǎn)潔的方式獲取

package main

import (
  "log"
  "os"
  "path/filepath"

  "golang.org/x/tools/go/packages"
)

func main() {
  cfg := &packages.Config{Mode: packages.NeedModule}
  pkgs, err := packages.Load(cfg, ".")
  if err != nil {
    log.Fatal(err)
  }

  root := pkgs[0].Module.Dir
  log.Println("項(xiàng)目根目錄:", root)
}

環(huán)境變量

  • 通過(guò)GOPATH或者自定義項(xiàng)目根路徑的環(huán)境變量
  • 利用系統(tǒng)自帶的環(huán)境變量和路徑

輸出打印

Go 語(yǔ)言通過(guò)標(biāo)準(zhǔn)庫(kù) fmt 包 實(shí)現(xiàn)格式化輸出,核心函數(shù)是 fmt.Print()、fmt.Println()、fmt.Printf(),其中 fmt.Printf() 支持最靈活的格式化輸出,是開(kāi)發(fā)中最常用的。

函數(shù)作用特點(diǎn)
fmt.Print(a...)普通輸出不換行,多個(gè)參數(shù)直接拼接
fmt.Println(a...)換行輸出自動(dòng)換行,參數(shù)間加空格
fmt.Printf(format, a...)格式化輸出按指定占位符格式輸出,不自動(dòng)換行

占位符以 % 開(kāi)頭,后跟類(lèi)型標(biāo)識(shí),是 fmt.Printf 的核心。

占位符作用
%v默認(rèn)格式輸出(最常用)
%+v輸出結(jié)構(gòu)體時(shí),顯示字段名
%#v輸出 Go 語(yǔ)法格式的值(調(diào)試用)
%T輸出值的類(lèi)型

代碼示例:

type User struct {Name string}
u := User{Name: "stark張宇"}

fmt.Printf("%v\n", u)   // {stark張宇}
fmt.Printf("%+v\n", u)  // {Name:stark張宇}
fmt.Printf("%T\n", u)   // main.User
fmt.Printf("%%\n")      // %

打印 布爾 / 字符串 / 指針占位符/十進(jìn)制整數(shù)

占位符作用
%t輸出布爾值:true/false
%s輸出字符串 / 字節(jié)切片
%q輸出帶雙引號(hào)的字符串
%p輸出指針地址(十六進(jìn)制)
%d10 進(jìn)制整數(shù)
%#v打印 map
str := "hello"
fmt.Printf("%s\n", str)  // hello
fmt.Printf("%q\n", str)  // "hello"
fmt.Printf("%p\n", &str) // 0x14000010200
fmt.Printf("map = %#v\n", m) //
fmt.Printf("執(zhí)行到:%s:%d\n", __FILE__, __LINE__)
fmt.Printf("a=%v, b=%v, c=%v\n", a, b, c)

數(shù)組Array、切片Slice

它們的共同點(diǎn)是都屬于集合類(lèi)的類(lèi)型,并且,它們的值也都可以用來(lái)存儲(chǔ)某一種類(lèi)型的值(或者說(shuō)元素)。它們最重要的不同是:數(shù)組類(lèi)型的值的長(zhǎng)度是固定的,而切片類(lèi)型的值是可變長(zhǎng)的。

切片的類(lèi)型字面量中只有元素的類(lèi)型,而沒(méi)有長(zhǎng)度,切片的長(zhǎng)度可以自動(dòng)地隨著其中元素?cái)?shù)量的增長(zhǎng)而增長(zhǎng),但不會(huì)隨著元素?cái)?shù)量的減少而減小。

數(shù)組和切片的關(guān)系: 可以把切片看做是對(duì)數(shù)組的一層簡(jiǎn)單的封裝,因?yàn)樵诿總€(gè)切片的底層數(shù)據(jù)結(jié)構(gòu)中,一定會(huì)包含一個(gè)數(shù)組。數(shù)組可以被叫做切片的底層數(shù)組,而切片也可以被看作是對(duì)數(shù)組的某個(gè)連續(xù)片段的引用。

切片的長(zhǎng)度和容量

s1 := make([]int, 5)
fmt.Printf("S1 length : %d , capacity %d ,value  %d \n ", len(s1), cap(s1), s1)
s2 := make([]int, 5, 8)
fmt.Printf("S2 length : %d , capacity %d ,value  %d \n ", len(s2), cap(s2), s2)

make函數(shù)初始化切片時(shí),如果不指明其容量,那么它就會(huì)和長(zhǎng)度一致。如果在初始化時(shí)指明了容量,那么切片的實(shí)際容量也就是它了。這也正是s2的容量是8的原因。

通過(guò)調(diào)用內(nèi)建函數(shù)len,得到數(shù)組和切片的長(zhǎng)度,通過(guò)調(diào)用內(nèi)建函數(shù)cap,我們可以得到它們的容量,但要注意,數(shù)組的容量永遠(yuǎn)等于其長(zhǎng)度,都是不可變的。

Go語(yǔ)言 1.18+ 后 ,當(dāng)執(zhí)行 append 時(shí),新長(zhǎng)度 len(s)+n > 當(dāng)前容量 cap(s) ,采用 分段式擴(kuò)容 + 內(nèi)存對(duì)齊,核心是 小切片翻倍、大切片 1.25 倍漸進(jìn)增長(zhǎng)

規(guī)則:小切片(<256):2 倍擴(kuò)容,大切片(≥256):~1.25 倍漸進(jìn)增長(zhǎng),這里的256有一個(gè)常見(jiàn)的誤區(qū),256 就是純數(shù)字 256,沒(méi)有任何單位(不是 KB、不是 MB),是元素個(gè)數(shù),cap的切片元素容量。

// 1. 容量 cap = 100 < 256 → 小切片
s1 := make([]int, 0, 100) 

// 2. 容量 cap = 300 > 256 → 大切片
s2 := make([]int, 0, 300) 

一個(gè)切片的底層數(shù)組永遠(yuǎn)不會(huì)被替換,當(dāng)切片在擴(kuò)容的時(shí)候 Go 語(yǔ)言一定會(huì)生成新的底層數(shù)組,但是它也同時(shí)生成了新的切片。需要注意的是在無(wú)需擴(kuò)容時(shí),append函數(shù)返回的是指向原底層數(shù)組的新切片,而在需要擴(kuò)容時(shí),append函數(shù)返回的是指向新底層數(shù)組的新切片。

只要新長(zhǎng)度不會(huì)超過(guò)切片的原容量,那么使用append函數(shù)對(duì)其追加元素的時(shí)候就不會(huì)引起擴(kuò)容,這只會(huì)使緊鄰切片窗口右邊的(底層數(shù)組中的)元素被新的元素替換掉。

擴(kuò)容時(shí)底層內(nèi)存變化

當(dāng)切片發(fā)生擴(kuò)容時(shí),判斷容量不夠,Go 會(huì)申請(qǐng)一塊新的、更大的連續(xù)內(nèi)存空間生成新底層數(shù)組,把舊數(shù)組里的所有元素復(fù)制到新數(shù)組,把新追加的元素也放進(jìn)去,切片內(nèi)部的指針從舊數(shù)組地址切換到新數(shù)組,舊數(shù)組只要還有引用,舊數(shù)組就會(huì)一直占內(nèi)存,如果沒(méi)有任何變量、切片再引用舊數(shù)組,Go 垃圾回收(GC)會(huì)在未來(lái)某個(gè)時(shí)間自動(dòng)回收

字典 Map

Map這個(gè)詞的本意在數(shù)學(xué)里指的是鍵值對(duì)的映射集合體,Go 語(yǔ)言的字典類(lèi)型其實(shí)是一個(gè)哈希表(hash table)的特定實(shí)現(xiàn),哈希表會(huì)先用哈希函數(shù)(hash function)把鍵值轉(zhuǎn)換為哈希值。哈希值通常是一個(gè)無(wú)符號(hào)的整數(shù),一個(gè)哈希表會(huì)持有一定數(shù)量的桶(bucket),元素就存在哈希桶中。

每個(gè)哈希桶都會(huì)把自己包含的所有鍵的哈希值存起來(lái),Go 語(yǔ)言會(huì)用被查找鍵的哈希值與這些哈希值逐個(gè)對(duì)比,看看是否有相等的,如果一個(gè)相等的都沒(méi)有,那么就說(shuō)明這個(gè)桶中沒(méi)有要查找的鍵值,這時(shí) Go 語(yǔ)言就會(huì)立刻返回結(jié)果了。

因?yàn)樯鲜鏊f(shuō)字典中鍵的查找原理,就要求鍵類(lèi)型的值必須要支持判等操作,要求鍵類(lèi)型不可以是函數(shù)類(lèi)型、字典類(lèi)型和切片類(lèi)型,而元素卻可以是任意類(lèi)型的, 否則在程序運(yùn)行過(guò)程中會(huì)引發(fā) panic。

var userAge map[string]int{
  "張三": 20,
  "李四": 25,
  "王五": 22,
}

for name, age := range userAge {
  fmt.Printf("姓名:%s,年齡:%d\n", name, age)
}

因?yàn)槊總€(gè)map的鍵都要進(jìn)行哈希值的計(jì)算和查找,所以寬度越小的類(lèi)型速度通常越快,類(lèi)型的寬度是指它的單個(gè)值需要占用的字節(jié)數(shù)。比如,bool、int8uint8類(lèi)型的一個(gè)值需要占用的字節(jié)數(shù)都是1,因此這些類(lèi)型的寬度就都是1。

總結(jié)

到此這篇關(guān)于Go語(yǔ)言必知的5個(gè)核心知識(shí)點(diǎn)之init、路徑、輸出、切片、Map的文章就介紹到這了,更多相關(guān)Go語(yǔ)言init、路徑、輸出、切片、Map內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 聊聊Go語(yǔ)言編譯github上的項(xiàng)目遇到的坑

    聊聊Go語(yǔ)言編譯github上的項(xiàng)目遇到的坑

    這篇文章主要介紹了解決Go語(yǔ)言編譯github上的項(xiàng)目遇到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 深入解析Sync.Pool如何提升Go程序性能

    深入解析Sync.Pool如何提升Go程序性能

    在并發(fā)編程中,資源的分配和回收是一個(gè)很重要的問(wèn)題。Go?語(yǔ)言的?Sync.Pool?是一個(gè)可以幫助我們優(yōu)化這個(gè)問(wèn)題的工具。本篇文章將會(huì)介紹?Sync.Pool?的用法、原理以及如何在項(xiàng)目中正確使用它,希望對(duì)大家有所幫助
    2023-05-05
  • 淺談Go Slice 高級(jí)實(shí)踐

    淺談Go Slice 高級(jí)實(shí)踐

    這篇文章主要介紹了淺談Go Slice 高級(jí)實(shí)踐,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • Go語(yǔ)言基礎(chǔ)go fmt命令使用示例詳解

    Go語(yǔ)言基礎(chǔ)go fmt命令使用示例詳解

    這篇文章主要為大家介紹了Go語(yǔ)言基礎(chǔ)go fmt命令的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • 深度解密Go語(yǔ)言中字符串的使用

    深度解密Go語(yǔ)言中字符串的使用

    在編程語(yǔ)言中,字符串發(fā)揮著重要的角色。這篇文章就來(lái)帶大家一起深度解密Go語(yǔ)言中的字符串,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-09-09
  • Go defer 原理和源碼剖析(推薦)

    Go defer 原理和源碼剖析(推薦)

    這篇文章主要介紹了Go defer 原理和源碼剖析,需要的朋友可以參考下
    2021-11-11
  • Golang程序中使用Prometheus的client_golang庫(kù)

    Golang程序中使用Prometheus的client_golang庫(kù)

    這篇文章主要介紹了Golang程序中使用Prometheus的client_golang庫(kù),Prometheus 是一個(gè)開(kāi)源的監(jiān)控和警報(bào)工具包,用于收集和處理應(yīng)用程序和系統(tǒng)的指標(biāo)數(shù)據(jù)。Prometheus 提供了多種客戶(hù)端庫(kù),可以輕松地集成到各種編程語(yǔ)言中
    2023-04-04
  • Go批量操作excel導(dǎo)入到mongodb的技巧

    Go批量操作excel導(dǎo)入到mongodb的技巧

    這篇文章主要介紹了Go批量操作excel導(dǎo)入到mongo,包括選擇命令行包,讀取配置連接數(shù)據(jù)庫(kù)的方法,本文示例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • Go?Interface接口初學(xué)者手冊(cè)

    Go?Interface接口初學(xué)者手冊(cè)

    這篇文章主要為大家介紹了Go?Interface接口的基礎(chǔ)用法實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go?error的使用方式詳解

    Go?error的使用方式詳解

    當(dāng)我們需要在Go項(xiàng)目中設(shè)計(jì)error,就不得不先知道Go error幾種常用方法,今天通過(guò)本文給大家介紹Go?error的使用方式詳解,感興趣的朋友一起看看吧
    2022-05-05

最新評(píng)論

菏泽市| 井陉县| 丰镇市| 盖州市| 林周县| 淅川县| 丘北县| 铁岭县| 韩城市| 菏泽市| 贵阳市| 嘉定区| 通山县| 辉南县| 惠东县| 余庆县| 务川| 蚌埠市| 茂名市| 紫阳县| 鞍山市| 兰溪市| 永城市| 凤凰县| 江北区| 金堂县| 绵竹市| 永胜县| 黎平县| 两当县| 临夏县| 聂拉木县| 凤山市| 陇南市| 彝良县| 安宁市| 大渡口区| 大竹县| 郸城县| 永安市| 永年县|