Golang 学习之路(一)程序结构
作者:互联网
程序结构
命名
Go 中的命名规范与 Java 类似,都是必须以一个字母或者下划线开头,不能是数字开头,后面可以接上任意的数字和字母,并且区分大小写。
有 25 个关键字,不能被用于自定义变量名
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
此外,还有大约 30 多个预定义的名字,主要对应内建的常量、类型和函数,可以自定义使用这些,但是要在注意使用引起混乱
内建常量: true false iota nil
内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内建函数: make len cap new append copy close delete
complex real imag
panic recover
由于 Go 中没有了 Java 中的 private
,public
关键字来限制方法的访问权限,所以在一个文件中,如果一个名字是大写字母开头的,像函数名,那么它将是导出的,也就是可以被外部的包访问,例如fmt.Printf()
可以在引用了fmt
包的地方访问 Printf
方法
变量
var
声明语句可以创建一个特点类型的变量,然后给变量附加一个名字,并且设置变量的初始值。语法如下
var 变量名字 类型 = 表达式
var s string = "apple"
其中,变量类型和表达式可以省略掉其中的一个,如果省略了类型,则会根据表达式的内容进行类型推断;如果省略了表达式的值,则会使用零值来初始化。
Go 语言的零值机制可以确保声明的变量都有一个良好定义的值,从而避免很多不必要的麻烦。因此在 Go 语言中不存在未初始化的变量,也就可以在不需要额外代码的情况下判断边界条件的合理性。
- 数值类型变量对应的零值是
0
- 布尔类型变量对应的零值是
false
- 字符串类型对应的零值是空字符串
""
- 接口或者引用类型(包括
slice
、指针、map
、chan
和函数)对应的零值是nil
- 数组或结构体等聚合类型对应的零值是每个元素或者字段对应类型的零值
例如:
var s string
fmt.Println(s) //输出的是空字符串"",啥都没有,而不是错误或者其他不可预知的行为
也可同时声明多个变量
var j,k,l int //三个变量都是int
var j,k,l,m = 1,false,"str",0.65 //int, bool, string,float
也可以通过函数返回的多个值进行初始化
var f, err = os.Open(name)
简短变量声明
在函数内部,可以通过 名字 := 表达式
的形式声明变量,变量的类型根据表达式来推断
a := 0.0
start := time.Now()
i, j := 0, 1
resp, err := http.Get(url)
简短变量声明语句中必须至少要声明一个变量,下面的情况将无法编译通过:
f, err := os.Open(infile)
// ...
f, err := os.Create(outfile) //会报错
指针
一个指针的值是另一个变量的地址。即指向对应变量在内存中的存储位置。
x := 2
p := &x
fmt.Println(p) //0xc000054190
fmt.Println(*p) //2
fmt.Println(x) //2
*p = 100
fmt.Println(p) //0xc000054190
fmt.Println(*p) //100
fmt.Println(x) //100
fmt.Println(x==*p) //true
任何指针的零值都是 nil
var p *int //初始化指针变量,零值为nil
fmt.Println(p) //<nil>
fmt.Println(p == nil) //true
var x, y string
fmt.Println(&x,&y) //0xc000032bc0 0xc000032bd0
//x,y的零值为"",但是分配了不同的内存地址
fmt.Println(&x == &y,&x == nil, &y == nil) //false false false
func f() *int {
y := 1
return &y
}
fmt.Println(f()) //0xc000054198
fmt.Println(f()) //0xc0000541c0
fmt.Println(f() == f()) //false
对于Go语言,严格意义上来讲,只有一种传递,也就是按值传递(by value)。当一个变量当作参数传递的时候,会创建一个变量的副本,然后传递给函数或者方法,你可以看到这个副本的地址和变量的地址是不一样的。
new 函数
内建的 new
函数也可以来创建变量,通过表达式 new(T)
将创建一个 T
类型的匿名变量,并且初始化零值,然后返回变量地址,返回的指针类型为 *T
p := new(int)
fmt.Println(p) //0xc000054190
*p = 1000
fmt.Println(*p) //1000
虽然 new
是 Go 的内建函数,但是并不是 25 个关键字之一,所以可以被用作变量名
每次调用new
函数都是返回一个新的变量地址
变量的生命周期
对于在包一级的声明的变量来说,它们的生命周期和整个程序运行的生命周期一致。相比之下,局部变量的生命周期则是动态的:每次从创建局部变量到不再被引用为止,然后可能被回收。函数的参数和返回值都是局部变量。
一个变量的有效周期只取决于是否可达,因此一个局部变量的可能会超出其作用域,同时,局部变量可能在函数返回之后依然存在。
编译器会自动选择是在栈上还是在堆上分配局部变量的存储空间,这个并不是由用var
声明还是用new
声明的
var global *int
func a() {
var x int
x = 1
global = &x
}
func b() {
x := new(int)
*x = 1
}
对于a()
来说,局部变量x
必须分配到堆上,因为在函数a
退出后,x
依然可以通过包一级的变量找到,也就是说,这个局部变量x
从函数a
中逃逸了。相反的,在函数b
中的局部变量x
在函数退出后就可以马上被回收了。编译器可以选择在栈上分配函数b
中的x
的内存空间,当然也可以选择在堆上分配,然后由 Go 的垃圾回收器GC
来回收
赋值
元组赋值
元组赋值允许同时更新多个变量的值。
在赋值之前,右边的所有表达式会先进行求职,然后再统一更新左边的值。
例如,求最大公约数
func gys(x, y int) int {
for y != 0 {
x, y = y, x%y
}
return x
}
fmt.Printf("最大公约数为%v\n",gys(16,24)) //最大公约数为8
计算斐波那契数列的第N个数
func fib(n int) int {
x, y := 0, 1
for i := 0; i < n; i++ {
x, y = y, x+y
}
return x
}
fmt.Printf("斐波那契数列的第%v个数是%v\n",15,fib(15)) //610
标签:学习,变量,int,fmt,程序结构,Golang,var,Println,零值 来源: https://blog.csdn.net/liu_1024_/article/details/93339048