【GO】函数,工程管理


函数

介绍go语言的函数相关知识


定义格式

函数构成代码执行的逻辑结构。在Go语言中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。

Go 语言函数定义格式如下:

func FuncName(/*参数列表*/) (o1 type1, o2 type2/*返回类型*/) {
    //函数体

    return v1, v2 //返回多个值
}

函数定义说明:

  • func:函数由关键字 func 开始声明
  • FuncName:函数名称,根据约定,函数名首字母小写即为private,大写即为public
  • 参数列表:函数可以有0个或多个参数,参数格式为:变量名 类型,如果有多个参数通过逗号分隔,不支持默认参数
  • 返回类型:
    ① 上面返回值声明了两个变量名o1和o2(命名返回参数),这个不是必须,可以只有类型没有变量名
    ② 如果只有一个返回值且不声明返回值变量,那么你可以省略,包括返回值的括号
    ③ 如果没有返回值,那么就直接省略最后的返回信息
    ④ 如果有返回值, 那么必须在函数的内部添加return语句

自定义函数

(1)无参无返回值

package main //必须

import "fmt"

func main() {
	//无参无返回值函数的调用: 函数名()
	MyFunc()
}

//无参无返回值函数的定义
func MyFunc() {
	a := 666
	fmt.Println("a = ", a)
}

(2)有参无返回值

go的参数类型有普通参数类型,和不定参数类型

以下是普通参数类型

package main //必须

import "fmt"

//有参无返回值函数的定义, 普通参数列表
//定义函数时, 在函数名后面()定义的参数叫形参
//参数传递,只能由实参传递给形参,不能反过来, 单向传递
func MyFunc01(a int) {
	//a = 111
	fmt.Println("a = ", a)
}

func MyFunc02(a int, b int) {
	fmt.Printf("a = %d, b = %d\n", a, b)
}

func MyFunc03(a, b int) {
	fmt.Printf("a = %d, b = %d\n", a, b)
}

func MyFunc04(a int, b string, c float64) {
}

func MyFunc05(a, b string, c float64, d, e int) {
}

func MyFunc06(a string, b string, c float64, d int, e int) {
}

func main() {
	//有参无返回值函数调用:  函数名(所需参数)
	//调用函数传递的参数叫实参
	MyFunc01(666)

	MyFunc02(666, 777)

}

以下是不定参数类型

package main //必须

import "fmt"

func MyFunc01(a int, b int) { //固定参数

}

//...int类型这样的类型, ...type不定参数类型
//注意:不定参数,一定(只能)放在形参中的最后一个参数
func MyFunc02(args ...int) { //传递的实参可以是0或多个
	fmt.Println("len(args) = ", len(args)) //获取用户传递参数的个数
	for i := 0; i < len(args); i++ {
		fmt.Printf("args[%d] = %d\n", i, args[i])
	}

	fmt.Println("==========================================")

	//返回2个值,第一个是下标,第二个是下标所对应的数
	for i, data := range args {
		fmt.Printf("args[%d] = %d\n", i, data)
	}

}

func main01() {
	//MyFunc01(666, 777)

	MyFunc02()
	fmt.Println("+++++++++++++++++++++++++++++++++++++++++++++++")
	MyFunc02(1)
	fmt.Println("+++++++++++++++++++++++++++++++++++++++++++++++")
	MyFunc02(1, 2, 3)
}

//固定参数一定要传参,不定参数根据需求传递
func MyFunc03(a int, args ...int) {
}

//注意:不定参数,一定(只能)放在形参中的最后一个参数
//func MyFunc04(args ...int, a int) {
//}

func main() {
	MyFunc03(111, 1, 2, 3)
}

不定参数的传递

package main //必须

import "fmt"

func myfunc(tmp ...int) {
	for _, data := range tmp {
		fmt.Println("data = ", data)
	}

}

func myfunc2(tmp ...int) {
	for _, data := range tmp {
		fmt.Println("data = ", data)
	}

}

func test(args ...int) {
	//全部元素传递给myfunc
	//myfunc(args...)

	//只想把后2个参数传递给另外一个函数使用
	myfunc2(args[:2]...) //args[0]~args[2](不包括数字args[2]), 传递过去
	myfunc2(args[2:]...) //从args[2]开始(包括本身),把后面所有元素传递过去
}

func main() {
	test(1, 2, 3, 4)
}

(3)无参有返回值

go语言函数返回值,支持一个返回值以及多个返回值,以下是只有一个返回值

package main //必须

import "fmt"

//无参有返回值:只有一个返回值
//有返回值的函数需要通过return中断函数,通过return返回
func myfunc01() int {
	return 666
}

//给返回值起一个变量名,go推荐写法
func myfunc02() (result int) {

	return 666
}

//给返回值起一个变量名,go推荐写法
//常用写法
func myfunc03() (result int) {

	result = 666
	return
}

func main() {
	//无参有返回值函数调用
	var a int
	a = myfunc01()
	fmt.Println("a = ", a)

	b := myfunc01()
	fmt.Println("b = ", b)

	c := myfunc03()
	fmt.Println("c = ", c)
}

多个返回值例子

package main //必须

import "fmt"

//多个返回值
func myfunc01() (int, int, int) {
	return 1, 2, 3
}

//go官方推荐写法
func myfunc02() (a int, b int, c int) {
	a, b, c = 111, 222, 333
	return
}

func myfunc03() (a, b, c int) {
	a, b, c = 111, 222, 333
	return
}

func main() {
	//函数调用
	a, b, c := myfunc02()
	fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

(4)有参有返回值

package main //必须

import "fmt"

//函数定义
func MaxAndMin(a, b int) (max, min int) {
	if a > b {
		max = a
		min = b
	} else {
		max = b
		min = a
	}

	return //有返回值的函数,必须通过return返回
}

func main() {
	max, min := MaxAndMin(10, 20)
	fmt.Printf("max = %d, min = %d\n", max, min)

	//通过匿名变量丢弃某个返回值
	a, _ := MaxAndMin(10, 20)
	fmt.Printf("a = %d\n", a)
}

递归函数

递归指函数可以直接或间接的调用自身。

递归函数通常有相同的结构:一个跳出条件和一个递归体。所谓跳出条件就是根据传入的参数判断是否需要停止递归,而递归体则是函数自身所做的一些处理。

先看看普通函数的调用过程

在这里插入图片描述

package main //必须

import "fmt"

func funcc(c int) {
	fmt.Println("c = ", c)
}

func funcb(b int) {

	funcc(b - 1)
	fmt.Println("b = ", b)
}

func funca(a int) {
	funcb(a - 1)
	fmt.Println("a = ", a)
}

func main() {
	funca(3) //函数调用
	fmt.Println("main")
}

递归函数的调用过程
在这里插入图片描述

package main //必须

import "fmt"

func test(a int) {
	if a == 1 { //函数终止调用的条件,非常重要
		fmt.Println("a = ", a)
		return //终止函数调用
	}

	//函数调用自身
	test(a - 1)

	fmt.Println("a = ", a)
}

func main() {
	test(3)
	fmt.Println("main")
}

递归函数示例:数字累加

在这里插入图片描述

package main //必须

import "fmt"

//实现1+2+3+……100
func test01() (sum int) {
	for i := 1; i <= 100; i++ {
		sum += i
	}

	return
}

func test02(i int) int {
	if i == 1 {
		return 1
	}

	return i + test02(i-1)
}

func test03(i int) int {
	if i == 100 {
		return 100
	}

	return i + test03(i+1)
}

func main() {
	var sum int
	//sum = test01()
	//sum = test02(100)
	sum = test03(1)
	fmt.Println("sum = ", sum)

}

函数类型

在Go语言中,函数也是一种数据类型,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。

package main //必须

import "fmt"

func Add(a, b int) int {
	return a + b
}

func Minus(a, b int) int {
	return a - b
}

//函数也是一种数据类型, 通过type给一个函数类型起名
//FuncType它是一个函数类型
type FuncType func(int, int) int //没有函数名字,没有{}

func main() {
	var result int
	result = Add(1, 1) //传统调用方式
	fmt.Println("result = ", result)

	//声明一个函数类型的变量,变量名叫fTest
	var fTest FuncType
	fTest = Add            //是变量就可以赋值
	result = fTest(10, 20) //等价于Add(10, 20)
	fmt.Println("result2 = ", result)

	fTest = Minus
	result = fTest(10, 5) //等价于Minus(10, 5)
	fmt.Println("result3 = ", result)
}

回调函数

注意回调函数和接口回调的区别

接口回调的理解:回调方 类A的一个方法a通过调用类B的一个方法b去调用类A的另一个方法c
参考链接https://blog.csdn.net/qq_33667439/article/details/78666979

回调函数的理解:将函数a作为参数传到另一个函数b里面,当函数a执行完之后,再执行传进去的函数b。这个过程就叫做异步回调。而同步回调指等待b执行完,再接着执行函数a
参考链接
https://blog.csdn.net/yilovexing/article/details/93192484
https://blog.csdn.net/angciyu/article/details/80794273

package main //必须

import "fmt"

type FuncType func(int, int) int

//实现加法
func Add(a, b int) int {
	return a + b
}

func Minus(a, b int) int {
	return a - b
}

func Mul(a, b int) int {
	return a * b
}

//回调函数,函数有一个参数是函数类型,这个函数就是回调函数
//计算器,可以进行四则运算
//多态,多种形态,调用同一个接口,不同的表现,可以实现不同表现,加减乘除
//现有想法,后面再实现功能
func Calc(a, b int, fTest FuncType) (result int) {
	fmt.Println("Calc")
	result = fTest(a, b) //这个函数还没有实现
	//result = Add(a, b) //Add()必须先定义后,才能调用
	return
}

func main() {
	a := Calc(1, 1, Mul)
	fmt.Println("a = ", a)
}

匿名函数和闭包

所谓闭包就是一个函数“捕获”了和它在同一作用域的其它常量和变量。这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。

在Go语言里,所有的匿名函数(Go语言规范中称之为函数字面量)都是闭包。匿名函数是指不需要定义函数名的一种函数实现方式,它并不是一个新概念,最早可以回溯到1958年的Lisp语言。

package main //必须

import "fmt"

func main() {
    i := 0
    str := "mike"

    //方式1
    f1 := func() { //匿名函数,无参无返回值
        //引用到函数外的变量
        fmt.Printf("方式1:i = %d, str = %s\n", i, str)
    }

    f1() //函数调用

    //方式1的另一种方式
    type FuncType func() //声明函数类型, 无参无返回值
    var f2 FuncType = f1
    f2() //函数调用

    //方式2
    var f3 FuncType = func() {
        fmt.Printf("方式2:i = %d, str = %s\n", i, str)
    }
    f3() //函数调用

    //方式3
    func() { //匿名函数,无参无返回值
        fmt.Printf("方式3:i = %d, str = %s\n", i, str)
    }() //别忘了后面的(), ()的作用是,此处直接调用此匿名函数

    //方式4, 匿名函数,有参有返回值
    v := func(a, b int) (result int) {
        result = a + b
        return
    }(1, 1) //别忘了后面的(1, 1), (1, 1)的作用是,此处直接调用此匿名函数, 并传参
    fmt.Println("v = ", v)

}


闭包捕获外部变量的特点

package main //必须

import "fmt"

func main() {
	a := 10
	str := "mike"

	func() {
		//闭包以引用方式捕获外部变量
		a = 666
		str = "go"
		fmt.Printf("内部:a = %d, str = %s\n", a, str)
	}() //()代表直接调用

	fmt.Printf("外部:a = %d, str = %s\n", a, str)

}

闭包(匿名函数)的特点

先看普通函数的调用特点

func test01() int {
	//函数被调用时,x才分配空间,才初始化为0
	var x int //没有初始化,值为0
	x++
	return x * x //函数调用完毕,x自动释放
}

func main01() {
	fmt.Println(test01())
	fmt.Println(test01())
	fmt.Println(test01())
	fmt.Println(test01())
}

闭包(匿名函数)的特点

// squares返回一个匿名函数,func() int
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
    var x int
    return func() int {//匿名函数
        x++ //捕获外部变量
        return x * x
    }
}

func main() {
    f := squares()
    fmt.Println(f()) // "1"
    fmt.Println(f()) // "4"
    fmt.Println(f()) // "9"
    fmt.Println(f()) // "16"
}

函数squares返回另一个类型为 func() int 的函数。对squares的一次调用会生成一个局部变量x并返回一个匿名函数。每次调用时匿名函数时,该函数都会先使x的值加1,再返回x的平方。第二次调用squares时,会生成第二个x变量,并返回一个新的匿名函数。新匿名函数操作的是第二个x变量。

通过这个例子,我们看到变量的生命周期不由它的作用域决定:squares返回后,变量x仍然隐式的存在于f中。

延迟调用defer

关键字 defer ⽤于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现在函数或方法的内部

package main //必须

import "fmt"

func main() {
	//defer延迟调用,main函数结束前调用
	defer fmt.Println("bbbbbbbbbbbbb")

	fmt.Println("aaaaaaaaaaaaaaa")
}

运行结果

aaaaaaaaaaaaaaa
bbbbbbbbbbbbb

defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。

多个defer的执行顺序

如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执⾏。

package main //必须

import "fmt"

func test(x int) {
	result := 100 / x

	fmt.Println("result = ", result)
}

func main() {

	defer fmt.Println("aaaaaaaaaaaaaa")   //第4执行

	defer fmt.Println("bbbbbbbbbbbbbb")  //第3执行

	//调用一个函数,导致内存出问题
	defer test(0)                        //第2执行

	defer fmt.Println("ccccccccccccccc")  //最先执行
}

 /*
    运行结果:
    cccccccc
    bbbbbbbb
    aaaaaaaa
    panic: runtime error: integer divide by zero
    */

defer和匿名函数结合使用

package main //必须

import "fmt"

func main() {
	a := 10
	b := 20

	//	defer func(a, b int) {
	//		fmt.Printf("a = %d, b = %d\n", a, b)
	//	}(a, b) //()代表调用此匿名函数, 把参数传递过去,已经先传递参数,只是没有调用

	defer func(a, b int) {
		fmt.Printf("a = %d, b = %d\n", a, b)
	}(10, 20) //()代表调用此匿名函数, 把参数传递过去,已经先传递参数,只是没有调用

	a = 111
	b = 222
	fmt.Printf("外部:a = %d, b = %d\n", a, b)
}

func main01() {
	a := 10
	b := 20

	defer func() {
		fmt.Printf("a = %d, b = %d\n", a, b)
	}() //()代表调用此匿名函数

	a = 111
	b = 222
	fmt.Printf("外部:a = %d, b = %d\n", a, b)
}

运行结果
外部:a = 111, b = 222
a = 10, b = 20

获得命令行参数

package main //必须

import "fmt"
import "os"

func main() {
	//接收用户传递的参数,都是以字符串方式传递
	list := os.Args

	n := len(list)
	fmt.Println("n = ", n)

	//xxx.exe a b
	for i := 0; i < n; i++ {
		fmt.Printf("list[%d] = %s\n", i, list[i])
	}

	for i, data := range list {
		fmt.Printf("list[%d] = %s\n", i, data)
	}
}

例子2

import (
    "fmt"
    "os"    //os.Args所需的包
)

func main() {
    args := os.Args //获取用户输入的所有参数

    //如果用户没有输入,或参数个数不够,则调用该函数提示用户
    if args == nil || len(args) < 2 {
        fmt.Println("err: xxx ip port")
        return
    }
    ip := args[1]   //获取输入的第一个参数
    port := args[2] //获取输入的第二个参数
    fmt.Printf("ip = %s, port = %s\n", ip, port)
}

在这里插入图片描述

作用域

作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。

(1)局部变量
在函数体内声明的变量、参数和返回值变量就是局部变量,它们的作用域只在函数体内:

func test(a, b int) {
    var c int
    a, b, c = 1, 2, 3
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

func main() {
    //a, b, c = 1, 2, 3 //err, a, b, c不属于此作用域
    {
        var i int
        i = 10
        fmt.Printf("i = %d\n", i)
    }

    //i = 20 //err, i不属于此作用域

    if a := 3; a == 3 {
        fmt.Println("a = ", a)
    }
    //a = 4 //err,a只能if内部使用
}

(2)全局变量
在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。

var a int //全局变量的声明

func test() {
    fmt.Printf("test a = %d\n", a)
}

func main() {
    a = 10
    fmt.Printf("main a = %d\n", a) //main a = 10

    test() //test a = 10
}

(3)不同作用域同名变量

在不同作用域可以声明同名的变量,其访问原则为:在同一个作用域内,就近原则访问最近的变量,如果此作用域没有此变量声明,则访问全局变量,如果全局变量也没有,则报错

var a int //全局变量的声明

func test01(a float32) {
    fmt.Printf("a type = %T\n", a) //a type = float32
}

func main() {
    fmt.Printf("a type = %T\n", a) //a type = int, 说明使用全局变量的a

    var a uint8 //局部变量声明

    {
        var a float64                  //局部变量声明
        fmt.Printf("a type = %T\n", a) //a type = float64
    }

    fmt.Printf("a type = %T\n", a) //a type = uint8

    test01(3.14)
    test02()
}

func test02() {
    fmt.Printf("a type = %T\n", a) //a type = int
}

工程管理

所有 Go 语言的程序都会组织成若干组文件,每组文件被称为一个包。这样每个包的代码都可以作为很小的复用单元,被其他项目引用。

一个包的源代码保存在一个或多个以.go为文件后缀名的源文件中,通常一个包所在目录路径的后缀是包的导入路径

package main //必须

//忽略此包
import _ "fmt"

func main() {

}

/*
//给包名起别名
import io "fmt"

func main() {
	io.Println("this is a test")
}
*/

/*
//.操作
import . "fmt" //调用函数,无需通过包名
import . "os"

func main() {
	Println("this is a test")
	Println("os.Args = ", Args)
}
*/

/*
//方式1
//import "fmt" //导入包,必须使用,否则编译不过
//import "os"

//方法2,常用
import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("this is a test")
	fmt.Println("os.Args = ", os.Args)
}
*/

自定义包

对于一个较大的应用程序,我们应该将它的功能性分隔成逻辑的单元,分别在不同的包里实现。我们创建的的自定义包最好放在GOPATH的src目录下(或者GOPATH src的某个子目录)。

在Go语言中,代码包中的源码文件名可以是任意的。但是,这些任意名称的源码文件都必须以包声明语句作为文件中的第一行,每个包都对应一个独立的名字空间:

package calc

包中成员以名称⾸字母⼤⼩写决定访问权限:
 public: ⾸字母⼤写,可被包外访问
 private: ⾸字母⼩写,仅包内成员可以访问

注意:同一个目录下不能定义不同的package。

main包

在 Go 语言里,命名为 main 的包具有特殊的含义。 Go 语言的编译程序会试图把这种名字的包编译为二进制可执行文件。所有用 Go 语言编译的可执行程序都必须有一个名叫 main 的包。一个可执行程序有且仅有一个 main 包。

当编译器发现某个包的名字为 main 时,它一定也会发现名为 main()的函数,否则不会创建可执行文件。 main()函数是程序的入口,所以,如果没有这个函数,程序就没有办法开始执行。程序编译时,会使用声明 main 包的代码所在的目录的目录名作为二进制可执行文件的文件名。

main函数和init函数

Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数。

Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

每个包可以包含任意多个 init 函数,这些函数都会在程序执行开始的时候被调用。所有被
编译器发现的 init 函数都会安排在 main 函数之前执行。 init 函数用在设置包、初始化变量或者其他要在程序运行前优先完成的引导工作。

程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。
有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。

当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:

在这里插入图片描述

示例代码目录结构:

在这里插入图片描述

main.go示例代码如下:

// main.go
package main

import (
    "fmt"
    "test"
)

func main() {
    fmt.Println("main.go main() is called")

    test.Test()
}

test.go示例代码如下:

//test.go
package test

import "fmt"

func init() {
    fmt.Println("test.go init() is called")
}

func Test() {
    fmt.Println("test.go Test() is called")
}

运行结果:

在这里插入图片描述

导入包

导入包需要使用关键字import,它会告诉编译器你想引用该位置的包内的代码。包的路径可以是相对路径,也可以是绝对路径。

//方法1
import "calc"
import "fmt"

//方法2
import (
    "calc"
    "fmt"
)

标准库中的包会在安装 Go 的位置找到。 Go 开发者创建的包会在 GOPATH 环境变量指定的目录里查找。 GOPATH 指定的这些目录就是开发者的个人工作空间。

如果编译器查遍 GOPATH 也没有找到要导入的包,那么在试图对程序执行 run 或者 build
的时候就会出错。

注意:如果导入包之后,未调用其中的函数或者类型将会报出编译错误。

在这里插入图片描述

点操作

这个点操作的含义是这个包导入之后在你调用这个包的函数时,可以省略前缀的包名。不需要再写包名.函数名,直接写函数名即可

import (
    //这个点操作的含义是这个包导入之后在你调用这个包的函数时,可以省略前缀的包名
    . "fmt"
)

func main() {
    Println("hello go")
}
别名操作

在导⼊时,可指定包成员访问⽅式,⽐如对包重命名,以避免同名冲突:

import (
    io "fmt" //fmt改为为io
)

func main() {
    io.Println("hello go") //通过io别名调用
_操作

有时,用户可能需要导入一个包,但是不需要引用这个包的标识符。在这种情况,可以使用空白标识符_来重命名这个导入:

import (
    _ "fmt"
)

_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数

工作区

工作区介绍

Go代码必须放在工作区中。工作区其实就是一个对应于特定工程的目录,它应包含3个子目录:src目录、pkg目录和bin目录。
 src目录:用于以代码包的形式组织并保存Go源码文件。(比如:.go .c .h .s等)
 pkg目录:用于存放经由go install命令构建安装后的代码包(包含Go库源码文件)的“.a”归档文件。
 bin目录:与pkg目录类似,在通过go install命令完成安装后,保存由Go命令源码文件生成的可执行文件。

目录src用于包含所有的源代码,是Go命令行工具一个强制的规则,而pkg和bin则无需手动创建,如果必要Go命令行工具在构建过程中会自动创建这些目录。

需要特别注意的是,只有当环境变量GOPATH中只包含一个工作区的目录路径时,go install命令才会把命令源码安装到当前工作区的bin目录下。若环境变量GOPATH中包含多个工作区的目录路径,像这样执行go install命令就会失效,此时必须设置环境变量GOBIN。

GOPATH设置

为了能够构建这个工程,需要先把所需工程的根目录加入到环境变量GOPATH中。否则,即使处于同一工作目录(工作区),代码之间也无法通过绝对代码包路径完成调用。

在实际开发环境中,工作目录往往有多个。这些工作目录的目录路径都需要添加至GOPATH。当有多个目录时,请注意分隔符,多个目录的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下。

案例:工程管理,同级目录

目录结构
在这里插入图片描述
main.go

package main //必须

func main() {
	test()
}

test.go

package main

import "fmt"

func test() {
	fmt.Println("this is a test func")
}

在这里插入图片描述

案例:工程管理,不同目录

在这里插入图片描述

在这里插入图片描述
main.go的代码

package main //必须

import (
	"calc"
	"fmt"
)

func init() {
	fmt.Println("this is main init")
}

func main() {
	a := calc.Add(10, 20)
	fmt.Println("a = ", a)

	fmt.Println("r = ", calc.Minus(10, 5))
}

calc.go的代码

package calc

import "fmt"

func init() {
	fmt.Println("this is calc init")
}

//func add(a, b int) int {
func Add(a, b int) int {
	return a + b
}

func Minus(a, b int) int {
	return a - b
}

要想读取到同级目录不同目录下的代码文件,首先需要设置gopath,保证src下的代码路径是在gopath路径下。可以通过go env命令,查看当前系统的gopath路径

如果使用goland ide编译器,还可以单独为项目设置gopath,而不只是使用系统的gopath,如下图所示

在这里插入图片描述

go install 的使用

另外可以使用go install命令,自动生成bin目录和pkg目录,前提条件是要设置环境变量GOBIN,可以用go env命令查看当前的GOBIN有没有设置

linux的设置方法
在这里插入图片描述
然后进入到源码目录,比如src目录下,输入go install即可自动生成pkg和bin目录

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug 挖掘机

支持洋子

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值