在$GOPATH/src
目录下创建一个hello.go
文件:
输入以下代码:
package main //程序的包名
//只要包含main函数的文件,就是main包
//main是主包,与工程文件名没有任何关系
/*
import "fmt" //导入格式化的包
import "time"
*/
//也可以一块导入包,导入多个包建议这种方案
import (
"fmt"
"time"
)
func main() { // 函数的}一定和函数名在一行,否则编译报错
fmt.Println("hello Go!")
time.Sleep(1 * time.Second)
}
终端运行:
go run
表示直接编译go语言并执行应用程序,一步完成;
也可以通过go build
先编译,然后再执行:
package main
定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main
。package main
表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。import "fmt"
告诉 Go 编译器这个程序需要使用 fmt
包(的函数,或其他元素),fmt
包实现了格式化 IO(输入/输出)的函数。func main()
是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。//
开头的单行注释。多行注释也叫块注释,均已以/*
开头,并以*/
结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。fmt.Println(...)
可以将字符串输出到控制台,并在最后自动增加换行字符 \n。 使用 fmt.Print(“hello, world\n”) 可以得到相同的结果。 Print
和 Println
这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。;
都可以,建议不加声明变量的一般形式是使用var
关键字;
var v_name v_type
v_name = value
var v_name v_type = value
package main
import "fmt"
func main() {
var a int = 5
fmt.Printf("a = %d\n", a)
}
var v_name = value
package main
import "fmt"
func main() {
var b = 10
fmt.Printf("b = %d\n", b)
}
v_name := value
:=
表示既初始化,又赋值package main
import "fmt"
func main() {
//var a int = 5
//var b = 10
a := 10
fmt.Printf("a = %d\n", a)
}
package main
import "fmt"
var a int = 100
var b = "abc"
// 这种分解的写法,一般用于声明全局变量
var (
c int
d bool
)
func main() {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
}
package main
import "fmt"
var a int = 100
var b = "abc"
c := 1.2
func main() {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
fmt.Println("c = ", c)
}
%T
格式化输出变量的类型package main
import "fmt"
func main() {
//默认值
var a int
fmt.Printf("a = %d\n", a)
fmt.Printf("type of a = %T\n", a)
var b int = 10
fmt.Printf("b = %d\n", b)
fmt.Printf("type of b = %T\n", b)
var c = "golang"
fmt.Printf("c = %s\n", c)
fmt.Printf("type of c = %T\n", c)
d := 20.15
fmt.Printf("d = %f\n", d)
fmt.Printf("type of d = %T\n", d)
}
var c, d int = 1, 2
var e, f = 123, "Golang"
g, h := 333, "需要在函数体内实现"
_
来接受赋值,表示赋值被废弃,_
不具备读特性package main
import "fmt"
var x, y int
// 这种分解的写法,一般用于声明全局变量
var (
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "Golang"
//这种不带声明格式的只能在函数体内声明
//g, h := 123, "需要在func函数体内实现"
func main() {
g, h := 333, "需要在函数体内实现"
//h
fmt.Println(x, y, a, b, c, d, e, f, g, h)
//不能对g变量再次做初始化声明
//g := 400
_, value := 7, 5 实际上7的赋值被废弃,变量 _ 不具备读特性
//fmt.Println(_)
fmt.Println(value)
var (
vv int = 15
cc bool = false
)
fmt.Println(vv, cc)
}
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
const identifier [type] = value
//显式类型定义
const b string = "abc"
//隐式类型定义:
const b = "abc"
例如:
package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "golang"
area = LENGTH * WIDTH
fmt.Println("面积为:", area)
fmt.Println(a, b, c)
}
const (
UNKNOW = 0
FEMALE = 1
MALE = 2
)
数字 0、1 和 2 分别代表未知性别、女性和男性。
len(), cap(), unsafe.Sizeof()
常量计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:package main
import (
"fmt"
"unsafe"
)
const (
a = "golang"
b = len(a)
c = unsafe.Sizeof(a)
)
func main() {
fmt.Println(a, b, c)
}
unsafe.Sizeof(a)
输出的结果是16 。
字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节。
有些概念有名字,并且有时候我们关注这些名字,甚至(特别)是在我们代码中。
const (
CCVisa = "Visa"
CCMasterCard = "MasterCard"
CCAmericanExpress = "American Express"
)
在其他时候,我们仅仅关注能把一个东西与其他的做区分。有些时候一件事没有本质上的意义。比如,我们在一个数据库表中存储产品,我们可能不想以 string 存储他们的分类。我们不关注这个分类是怎样命名的,此外,该名字在市场上一直在变化,我们仅仅关注它们是怎么彼此区分的。
const (
CategoryBooks = 0
CategoryHealth = 1
CategoryClothing = 2
)
使用 0, 1, 和 2 代替,我们也可以选择 17, 43, 和 61。这些值是任意的
在 Go,常量有许多微妙之处。当用好了,可以使得代码非常优雅且易维护的。
自增长
iota
标示符,它简化了常量用于增长数字的定义,给以上相同的值以准确的分类。const()
添加一个关键字iota
,只在第一行添加iota,之后的每行的iota
都会累加1,第一行的iota
默认值是0const (
CategoryBooks = iota // 0
CategoryHealth // 1
CategoryClothing // 2
)
iota和表达式
iota
可以做更多事情,而不仅仅是 increment。更精确地说,iota
总是用于 increment,但是它可以用于表达式,在常量中的存储结果值。package main
import (
"fmt"
)
type Allergen int
const (
IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001
IgChocolate // 1 << 1 which is 00000010
IgNuts // 1 << 2 which is 00000100
IgStrawberries // 1 << 3 which is 00001000
IgShellfish // 1 << 4 which is 00010000
)
func main() {
fmt.Println(IgEggs | IgChocolate | IgShellfish)
}
00010011
,它对应十进制的 19。package main
import (
"fmt"
)
type ByteSize float64
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota) // 1 << (10 * 1)
MB
GB
TB
PB
EB
ZB
YB
)
func main() {
fmt.Println(KB, MB, GB)
}
package main
import (
"fmt"
)
const (
Apple, Banana = iota + 1, iota + 2
Cherimoya, Durian
Elderberry, Fig
)
func main() {
fmt.Println(Apple, Banana, Cherimoya, Durian)
}
package main
import (
"fmt"
)
const (
a, b = iota + 1, iota + 2
c, d
e, f
g, h = iota * 2, iota * 3
i, j
)
func main() {
fmt.Println("a = ", a, "b = ", b)
fmt.Println("c = ", c, "d = ", d)
fmt.Println("e = ", e, "f = ", f)
fmt.Println("g = ", g, "h = ", h)
fmt.Println("i = ", i, "j = ", j)
}
Go函数的定义:
func 函数名(形参1 类型, 形参2 类型, ...) (返回值1类型, 返回值2类型, ...){
// 函数体
return 返回值1, 返回值2
}
Go 函数可以返回多个值
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("golang", "c++")
fmt.Println(a, b)
}
package main
import "fmt"
func prt(a string, b int) (r1 int, r2 int) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
fmt.Println(r1, r2) // 没赋值之前,r1和r2默认值为0,相当于函数中的形参
//给有名称的返回值变量赋值
r1 = 1000
r2 = 2000
return
}
func main() {
a, b := prt("golang", 123)
fmt.Println(a, b)
}
func swap(x, y string) (r1, r2 string) {
return y, x
}
执行顺序
例子:
module function_go
go 1.20
package Initlib1
import "fmt"
// lib1提供的API
func lib1Test() {
fmt.Println("lib1Test()...")
}
func init() {
fmt.Println("lib1")
}
package Initlib2
import "fmt"
// lib2提供的API
func lib2Test() {
fmt.Println("lib2Test()...")
}
func init() {
fmt.Println("lib2")
}
go.mod
中模块名是function_go
,导入包的路径就是function_go/InitLib1
包名.方法()
package main
import (
"function_go/InitLib1"
"function_go/InitLib2"
)
func main() {
InitLib1.Lib1Test()
InitLib2.Lib2Test()
}
main.go
中使用import
导本地包的时候,一定要在包名前加上该包路径相对于GOPATH
的路径运行结果
lib1
lib2
libmain init
libmian main
package InitLib1
import (
"fmt"
"goProject/function_go/InitLib2"
)
// lib2提供的API
func lib2Test() {
fmt.Println("lib2Test()...")
}
func init() {
fmt.Println("lib1")
}
输出结果:
lib2
lib1
libmain init
libmian main
main包以及Lib1包都导入了Lib2,但是只出现一次,并且最先输出
说明如果一个包会被多个包同时导入,那么它只会被导入一次,而先输出lib2是因为main包中导入Lib1时,Lib1又导入了Lib2,会首先初始化Lib2包的东西
如果一个函数名首字母是大写的,说明此函数是对外开放的,如果是小写的,只能在当前包内调用,无法对外开放
_
就代表匿名导入package main
import (
_ "function_go/InitLib1" //匿名导包
_ "function_go/InitLib2"
)
func main() {
InitLib1.Lib1Test()
InitLib2.Lib2Test()
}
package main
import (
"function_go/InitLib1"
myLib2 "function_go/InitLib2" //给包起别名
)
func main() {
InitLib1.Lib1Test()
myLib2.Lib2Test()
}
.
,表示将包中所有内容导入当前包,可以直接调用方法,不需要在前面指定包名package main
import (
"goProject/function_go/InitLib1"
. "goProject/function_go/InitLib2" //将改包所有的方法导入main包
)
func main() {
InitLib1.Lib1Test()
Lib2Test() //直接调用方法
}
项目结构:
fun_go和function_go是两个不同的模块,如果要在fun_go下的main.go中导入function_go/InitLib1包,也需要指定模块名:
package main
import (
"fmt"
"function_go/InitLib1" // 指定模块名
)
func main() {
InitLib1.Lib1Test()
fmt.Println("go")
}
函数如果使用参数,该变量可称为函数的形参;形参就像定义在函数体内的局部变量。
调用函数,可以通过两种方式来传递参数:
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
以下定义了 swap() 函数:
package main
import "fmt"
func swap(x, y int) {
var temp int
temp = x
x = y
y = temp
}
func main() {
a := 100
b := 200
fmt.Println("交换前a的值:", a)
fmt.Println("交换前b的值:", b)
swap(a, b)
fmt.Println("交换后a的值:", a)
fmt.Println("交换后b的值:", b)
}
指针
&
,放到一个变量前使用就会返回相应变量的内存地址。package main
import "fmt"
func main() {
a := 100
fmt.Println(&a)
}
package main
import "fmt"
func swap(x, y *int) {
var temp int
temp = *x
*x = *y
*y = temp
}
func main() {
a := 100
b := 200
fmt.Println("交换前a的值:", a)
fmt.Println("交换前b的值:", b)
swap(&a, &b)
fmt.Println("交换后a的值:", a)
fmt.Println("交换后b的值:", b)
}
package main
import "fmt"
func main() {
a := 100
var p *int = &a
var pp **int = &p
fmt.Println(&p)
fmt.Println(pp)
}