- 函数、变量、常量、类型、语句标签和包都遵循:
字母或下划线开头,后面跟随任意数量的字符,数字和下划线,并且区分大小写
。 - 若实体在函数中声明,那么实体只在函数局部有效。如果声明在函数外,将对包内所有源文件可见。
- 实体的第一个字母的
大小写
决定其可见性是否跨包,大写表示对包外是可见和可访问的。
package main
import "fmt"
const commonConstant = "common"
func main() {
var a = commonConstant
var b string // 会默认初始化为""
var c string = "c"
var d = "d"
e := "e" // 短变量声明
x, y := true, 2.3 // 一次定义多个变量
fmt.Println(a) // common
fmt.Println(b) // ""
fmt.Println(c) // c
fmt.Println(d) // d
fmt.Println(e) // e
fmt.Println(x)
fmt.Println(y)
}
- 定义变量时如果不指定初始值,那么默认初始化为零值(int为0,string为""...)
指针的值是一个变量的地址
。使用指针可以在无须知道变量名称的情况下,简洁读取或更新变量的值。
package main
import "fmt"
func pointer() {
a := "1"
p := &a // &z表示获取一个指向整型变量的指针,类型是整型指针(*int)
fmt.Println(*p) // *p表示p指向的变量
*p = "2"
fmt.Println(a) // 2
var x, y int
fmt.Println(&x == &x, &x == &y, &x == nil) // true false false
q := 1
incr(&q) // &q所指向的值加1,不依赖返回值就能实现更新
fmt.Println(q) // 2
fmt.Println(&q) // 指针地址
fmt.Println(incr(&q)) // 3
}
func incr(p *int) int {
*p++ // 递增p所指向的值,p本身不变(p是一个指针地址)
return *p
}
func flagTest() {
var str string
flag.StringVar(&str, "s", "", "将空格替换为指定分隔符") // 传入变量的指针,不需要返回值就能改变str的值
flag.Parse() // 解析用户传入的命令行参数
if str != "" {
fmt.Println(strings.Join(flag.Args(), str)) // 参数是 ./main -s / a bc 输出 a/bc
} else {
fmt.Println(flag.Args())
}
}
&z表示获取一个指向整型变量z的指针,类型是整型指针(*int)
*p
表示指针p指向的变量,*p
代表一个变量。而p则代表指针,是一个0x开头的地址。为什么使用指针? > > a. 相比java中都是
值传递(对形参的修改不会影响实参)
,通过传递指针而不是复制整个数据结构,可以节省时间和内存。 > > b. 将变量的指针传递给函数,可以在函数内部直接修改变量的值
,而不需要返回值。
通过new(type)
方式创建变量的指针,常用于初始化复杂类型的指针
,便于在堆上分配内存。
package main
import "fmt"
func main() {
p := new(int)
x := new(int)
fmt.Println(p) // 指针的地址
fmt.Println(p == x) // 每new一次每次的地址都不同
fmt.Println(*p) // 初始为0
p := new(Person)
fmt.Println(p.Age) // 会被初始化为零值
}
type Person struct {
Name string
Age int
}
new创建的变量是被初始化为
零值
的,Person中的Name和Age分别被初始化为""和0。
生命周期:程序执行过程中变量存在的时间段。
包级别
的变量生命周期伴随着整个程序
的执行时间。
局部变量
的生命周期,每次执行声明语句时创建一个新的实体,一直存在到不可访问
。通过可达性算法来判断变量是否能够回收。与
JVM从根对象
出发判断类似,将包级别的变量
、当前执行函数的局部变量
作为源头,通过指针或其他方式
的引用可以找到的变量则被视为可达。
package main
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
- 针对f()函数,尽管在其返回后还能通过global访问,这种情况成为
x从f中逃逸
。- 当g()函数返回时,*y变得不可访问,可以被回收。这种情况被
*y无法从g中逃逸
。
func assign() {
var x int
x = 1 // 有名称的变量
fmt.Printf("x: %d\n", x)
y := &x
*y = 2 // 通过指针间接赋值
fmt.Printf("*y: %d\n", *y)
p := new(Person)
p.Name = "lucy" // 结构体成员
fmt.Printf("name: %s\n", p.Name)
s := []int{1, 2, 3} // 数组或slice或map的元素
fmt.Printf("slice: %d\n", s[1])
c := 1
c++
c--
fmt.Printf("c: %d\n", c)
a := 0
b := 1
a, b, c = b, a, a+b // 多重赋值,即一次性赋值多个变量,并且变量支持右侧表达式推演
fmt.Printf("a: %d, b: %d, c: %d\n", a, b, c)
}
- 有名称的变量赋值
- 通过指针间接赋值
- 结构体成员赋值
- 数组或slice或map元素赋值
- 多重赋值,一次赋值多个变量(左侧的变量个数需要和函数的返回值一样多,不需要的值赋值给
空标识符_
)
在go语言中包的作用和其他语言的库或模块作用类似,用于支持模块化、封装、编译隔离和重用
。
go init mod <module name> // 初始化module
go mod tidy // 整理依赖
.
├── README.md
├── src
│ └── source.go
├── go.mod
├── main.go
└── chapter1
├── README.md
└── chapter1.go
package main
import (
"go-in-action/chapter1" // <moduleName>/<folderName>
)
func main() {
chapter1.Main()
}
- 大写开头的func才能够被其他go文件引用
- import引入的路径只需要包含文件夹,不需要包含文件名称