GO 学习笔记(二)
1 语言
1.1 类型
1.1.1 变量
- Go 是静态类型语 ,不能在运 期改变变量类型。
- var 定义变量,自动初始化为零值, 如果手动提供初始化,可以省略类型
- 在函数内部用 := 定义变量更加方便
- 未使用的局部变量将会报错
- 统一代码块内对同名变量重新定义等于重新赋值,不同代码块对同名变量重新定义等于新建
- := 必须产生新变量
package main
import (
"fmt"
)
func main() {
var a int
var b float32 = 2.1
var c = "hello"
d := 3
var e, f, g int = 1, 2, 3
var (
h int = 1
i = 2.0
)
j, k := "world", 3
_, l := 1, 2
fmt.Println(a, b, c, d, e, f, g, h, i, j, k)
_ = l
m := 1
fmt.Println(&m, m) //0xc420070208, 1
m, n := 2, 3
// m := 4 不允许,因为:=没有产生新的变量
_ = n
fmt.Println(&m, m) //0xc420070208, 2
{
m := 3
fmt.Println(&m, m) //0xc420070220, 3
}
}
1.1.2 常量
- 常量值必须是编译期可确定的数字、字符串、布尔值。
- 不支持持 1UL、2LL 这样的类型后缀。
- 在常量组中,不提供初始值,则默认与上一个值相同
- 常量值可以是在编译器能确定内容的函数
package main
import (
"fmt"
)
const (
a = 1
b = true
c = "hello"
d = len(c)
e
)
func main() {
fmt.Println(a, b, c, d, e) //1 true hello 5 5
}
1.1.3 枚举
package main
import (
"fmt"
)
type Color int //自定义类型
const (
// iota 自增值
Sunday = iota
Monday
Tuesday
Wednesday
)
const (
_ = iota
KB = 1 << (10 * iota)
MB = 1 << (10 * iota)
)
const (
A, B = iota, 10*iota
C, D
)
const (
Red Color = iota
Blue
)
var a = 2 //未使用的全局变量不会出错
func test (c Color) {}
func main() {
fmt.Println(Sunday, Monday, Tuesday) //0 1 2
fmt.Println(KB, MB, A, B, C, D) //1024 1048576 0 0 1 10
fmt.Println(Red, Blue) //0 1
c1 := Red
c2 := 1
test(c1)
// test(c2) // 出错, int 与 Color 不是一个类型
_ = c2
test(1) // 自动转换成Red
}
1.1.4 基本类型
- 空指针nil,不是NULL
- 值类型 bool, byte, rune, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, complex64, complext128, uintptr, array, struct, string
- 引用类型 slice, map, channel, interface, function
- new([]int) 计算类型大小,分配零值,返回指针; make([]int) 分配内存,创建数据结构,返回对象
- 不支持隐式类型转换
- 不可以用其他值代表bool
- 字符串 可以 s[i] 访问内部元素, 不可以对内部元素进行赋值s[i] = 1, 不可以访问内部元素的指针 &s[i], 支持` 和" 两种定义形式, 链接跨行字符串
+
必须放在上一行末尾,修改字符串肯定会重新分配内存,
1.1.5 指针类型
- 默认nil,没有NULL常量
- 操作符
&
取变量地址,* 操作符通过指针访问目标对象 - 不支持指针运算,不支持
->
操作符, 可以直接.
访问对象
1.1.6 自定义类型
可以将类型分为命名类型和未命名类型, 命名类型包括bool, int, string, 等, 未命名类型包括array, map, slice等
具有相同声明的未命名类型被视为同一类:
- 具有相同基本类型的指针
- 具有相同元素和长度的array
- 具有相同key-value的map
- 具有相同元素类型的slice
- 具有相同元素类型和方向的channel
- 具有相同字段序列的匿名struct
- 签名相同的fuction(不包括参数名)
- 方法集相同的interface
type
关键字可以在局部或者全局定义类型,新定义的类型并不是被定义类型的别名, 除非目标是非命名类型,否则必须显示转换
type bigint int64
var a bigint = 2
var b int64 = 3
a = bigint(b)
1.2 表达式
1.2.1 运算符
++
--
是语句而非表达式- 没有
~
, 可以用^
取反
1.2.2 初始化
- 初始化复合对象,必须使用类型标签,且做大括号必须在类型的尾部
var a = []int {1,2,3}
`var a = struct { v1 int } {10} - 初始化值可以用
,
分割,但是最后一行必须以,
或者}
结尾
var a = []int {
1,
2,
3,
}
var b = []int {
1,
2,
3}
1.2.3 控制流
if
- 可以省略条件表达式的括号
- 支持初始化语句,可定义局部变量
{
必须放在条件表达式右面- 不支持三元操作符
for
for i := 0; i < 100; i++ {
// do
}
i := 0
for i < 10 {
// do
i++
}
for {
//dp
}
range
迭代操作,返回 (索引,值) 或 (键,值)
- range 会复制 对象的值
package main
import (
"fmt"
)
func main() {
a := []int {1,2,3,4}
fmt.Println(a[1], &a[1])
for i, v := range a {
fmt.Println(v, &v, &a[i])
}
}
/*
1 0xc42000e140 0xc42000a2a0
2 0xc42000e140 0xc42000a2a8
3 0xc42000e140 0xc42000a2b0
4 0xc42000e140 0xc42000a2b8
*/
switch
x := []int{1, 2, 3}
i := 2
switch i {
case x[1]:
println("a")
case 1, 3:
println("b")
break
default:
println("c")
}
// a 为什么呢? 因为只会运行一个case,默认省略了break
goto break continue
package main
import (
"fmt"
)
func main(){
FOR1:
for i := 0; i < 100; i++ {
FOR2:
for j := 0; j < 100; j++ {
fmt.Println(i,j)
if i == 10 {
break FOR2
}
if i == 20 {
continue FOR1
}
if i == 30 {
goto EXIT
}
}
}
EXIT:
fmt.Println("exit")
}
1.3 函数
1.3.1 函数定义
- 不支持重载,默认参数,嵌套
- 支持不定长参数,支持多返回,支持命名返回参数,支持匿名和闭包
{
依旧不能另起一行
1.3.2 可变参数
- 可变参数本质是slice, 只能有一个且只能为最后一个
- 使用slice对象做变参的时候,必须展开
- defer 延迟调用
package main
import (
"fmt"
)
type MyFunc func(int, string) int
func test1 (mf MyFunc, a int, b string) (c int){
c = mf(a, b)
return
}
func test2 (mf func(int) int, a int) int {
return mf(a)
}
func main(){
a := func(a int, b string) int {
return a + len(b)
}
fmt.Println(test1(a, 1, "hello"))
fmt.Println(test2(func(a int) int { a++ ; return a}, 1))
}
func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
func main() {
s := []int{1, 2, 3}
println(test("sum: %d", s...))
}