go init函数详解

Go init 函数

1. 介绍

golang中有个神奇的函数init,该函数会在所有程序执行开始前被调用,每个包可以包含多个init函数,所有被编辑器识别到的init函数都会在main函数执行前被调用。通常被用来注册一个程序需要使用的依赖,如mysql注册,配置文件加载等。

2. 作用

  • 初始化不能采用初始化表达式初始化的变量。
  • 程序运行前的注册。
  • 实现sync.Once功能。
  • 其他

3. 特点

  • init函数先于main函数自动执行,不能被其他函数调用;
  • init函数没有输入参数、返回值;
  • 每个包可以有多个init函数;
  • 包的每个源文件也可以有多个init函数,这点比较特殊;
  • 同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。
  • 不同包的init函数按照包导入的依赖关系决定执行顺序。

4. 测试

golang程序初始化先于main函数执行,由runtime进行初始化,初始化顺序如下:

初始化导入的包(包的初始化顺序并不是按导入顺序(“从上到下”)执行的,runtime需要解析包依赖关系,没有依赖的包最先初始化,与变量初始化依赖关系类似,参见golang变量的初始化);
初始化包作用域的变量(该作用域的变量的初始化也并非按照“从上到下、从左到右”的顺序,runtime解析变量依赖关系,没有依赖的变量最先初始化,参见golang变量的初始化);
执行包的init函数;

package main                                                                                                                     

import (
   "fmt"              
)

var T int64 = a()

func init() {
   fmt.Println("init in main.go ")
}

func a() int64 {
   fmt.Println("calling a()")
   return 2
}
func main() {                  
   fmt.Println("calling main")     
}
[root@localhost init]# go run init2.go
calling a()
init in main.go 
calling main

初始化顺序:变量初始化->init()->main()

示例2:
pack.go

package pack                                                                                                                     

import (
   "fmt"
   "test_util"
)

var Pack int = 6               

func init() {
   a := test_util.Util        
   fmt.Println("init pack ", a)    
} 

test_util.go

test_util.go

package test_util                                                                                                                

import "fmt"

var Util int = 5

func init() {
   fmt.Println("init test_util")
}  

main.go

package main                                                                                                                     

import (
   "fmt"
   "pack"
   "test_util"                
)

func main() {                  
   fmt.Println(pack.Pack)     
   fmt.Println(test_util.Util)
}

输出:

init test_util
init pack  5
6
5

由于pack包的初始化依赖test_util,因此运行时先初始化test_util再初始化pack包;
示例3:
sandbox.go

package main
import "fmt"
var _ int64 = s()
func init() {
   fmt.Println("init in sandbox.go")
}
func s() int64 {
   fmt.Println("calling s() in sandbox.go")
   return 1
}
func main() {
   fmt.Println("main")
}

a.go

package main
import "fmt"
var _ int64 = a()
func init() {
   fmt.Println("init in a.go")
}
func a() int64 {
   fmt.Println("calling a() in a.go")
   return 2
}

z.go

package main
import "fmt"
var _ int64 = z()
func init() {
   fmt.Println("init in z.go")
}
func z() int64 {
   fmt.Println("calling z() in z.go")
   return 3
}
[root@localhost test2]# go run sandbox.go a.go z.go 
calling s() in sandbox.go
calling a() in a.go
calling z() in z.go
init in sandbox.go
init in a.go
init in z.go
main

同一个包不同源文件的init函数执行顺序,golang spec没做说明,以上述程序输出来看,执行顺序是源文件名称的字典序。
示例4:

package main
import "fmt"
func init() {
   fmt.Println("init")
}
func main() {
   init()
}

init函数不可以被调用,上面代码会提示:undefined: init

示例5:

package main
import "fmt"
func init() {
   fmt.Println("init 1")
}
func init() {
   fmt.Println("init 2")
}
func main() {
   fmt.Println("main")
}

输出:

init 1
init 2
main

init函数比较特殊,可以在包里被多次定义。

示例6:

var initArg [20]int
func init() {
   initArg[0] = 10
   for i := 1; i < len(initArg); i++ {
       initArg[i] = initArg[i-1] * 2
   }
}

init函数的主要用途:初始化不能使用初始化表达式初始化的变量

示例7:

import _ "net/http/pprof"

golang对没有使用的导入包会编译报错,但是有时我们只想调用该包的init函数,不使用包导出的变量或者方法,这时就采用上面的导入方案。

执行上述导入后,init函数会启动一个异步协程采集该进程实例的资源占用情况,并以http服务接口方式提供给用户查询。

参考:

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
HAL_GPIO_Init 函数是一个在 STM32 系列单片机的 HAL 库中定义的函数,用于初始化 GPIO 口的配置。 其函数原型如下: ```c HAL_GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_Init) ``` 其中,GPIO_TypeDef* GPIOx 表示要初始化的 GPIO 口所在的 GPIOx 端口,例如 GPIOA、GPIOB 等,GPIO_InitTypeDef* GPIO_Init 表示 GPIO 口的配置信息,包括 GPIO 口的模式、输出类型、上拉下拉等参数。 在使用 HAL_GPIO_Init 函数初始化 GPIO 口时,需要先创建一个 GPIO_InitTypeDef 的结构体,然后根据需要设置 GPIO 口的各个参数,最后将结构体作为参数传入 HAL_GPIO_Init 函数中,即可完成 GPIO 口的初始化配置。 以下是 GPIO_InitTypeDef 结构体的定义: ```c typedef struct { uint32_t Pin; /* Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins_define */ uint32_t Mode; /* Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIO_mode_define */ uint32_t Pull; /* Specifies the Pull-up or Pull-Down activation for the selected pins. This parameter can be a value of @ref GPIO_pull_define */ uint32_t Speed; /* Specifies the speed for the selected pins. This parameter can be a value of @ref GPIO_speed_define */ uint32_t Alternate; /* Peripheral to be connected to the selected pins. This parameter can be a value of @ref GPIOEx_Alternate_function_selection */ } GPIO_InitTypeDef; ``` 其中,Pin 表示要配置的 GPIO 口的引脚编号,Mode 表示 GPIO 口的工作模式,Pull 表示 GPIO 口的上拉/下拉模式,Speed 表示 GPIO 口的速度,Alternate 表示 GPIO 口的复用功能。 总之,HAL_GPIO_Init 函数是 STM32 系列单片机 HAL 库中一个非常重要的函数,它能够帮助我们实现 GPIO 口的初始化和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值