一、变量定义
四种方式:
- 完全体:var name type
- 类型推断:var name = value
- 最简体:name := value(仅用于函数内变量,包内变量不行)
- 变量聚合定义:var( name1=value1 name2=value2 )
package main import "fmt" // 定义包内变量 var ( aa = 1 bb = "lanxin" ) // 定义变量,只使用默认初值 func variableZeroValue() { var a int // 0 var s string // "" var b bool // false fmt.Println(a, s, b) } // 定义变量,赋初值 func variableInitialValue() { var a, b int = 1, 2 b = 3 var s string = "lanxin" fmt.Println(a, b, s) } // 类型推断 func variableTypeDeduction() { var a, b, c, d = 1, 2, true, "hi" fmt.Println(a, b, c, d) } // 最简定义变量方式 func variableShorter() { a, b, c, d := 3, 2, true, "hi" fmt.Println(a, b, c, d) } func main() { variableZeroValue() variableInitialValue() variableTypeDeduction() variableShorter() fmt.Println(aa, bb) }
二、内建变量类型
8 类变量类型:
- bool
- string
- (u)int、(u)int8、(u)int16、(u)int32、(u)int64
- uintptr 指针
- byte
- rune 字符型,32 位,类比 char
- float32、float64
- complex32、complex64 复数 i = √-1
注意:类型转换必须强制转,转化成的类型可以不带小括号,如下:
// 强制类型转换 func triangle() { var a, b int = 3, 4 var c int // float64 和 int 可以不加小括号,也可以加上 // 开方内建函数定义:func Sqrt(x float64) float64 c = int(math.Sqrt(float64(a*a + b*b))) fmt.Println(c) }
三、常量与枚举
- 常量定义方式(常量必须有 value)
- 完全体:const name type = value
- 后续的使用自动补类型:const name = value
- 枚举定义方式(Go 没有枚举,用 const 块来定义)
- 自定义枚举:const ( name1=value1 name2=value2 )
- iota 表达式枚举:const ( name1=iota表达式 name2 )
// 常量 func consts() { // 指定类型 const filename string = "filename-const" // 不指定类型,表示类型不定 const a, b = 3, 4 var c int // 由于类型不定,所以这里不需要强转,如果定义为 const a, b int = 3, 4,则需要强转 c = int(math.Sqrt(a*a + b*b)) fmt.Println(filename, a, b, c) } // 枚举 func enums() { // 使用 const 块来实现枚举 const ( java = 0 cpp = 1 c = 2 ) fmt.Println(java, cpp, c) // 0 1 2 // 使用 iota 块来实现自增枚举 const ( java1 = iota cpp1 c1 ) fmt.Println(java1, cpp1, c1) // 0 1 2 }
四、条件语句
if:变量可以定义在 if 块内,其作用域就只在 if 块内。
package main import ( "fmt" "io/ioutil" ) func readFile() { const filename = "abc.txt" // Go 函数可以返回两个值 // func ReadFile(filename string) ([]byte, error) contents, err := ioutil.ReadFile(filename) if err != nil { fmt.Println(err) } else { // contents 是 []byte, 用%s 可以打印出来 fmt.Printf("%s", contents) } // if 语句外部可访问 fmt.Printf("%s", contents) } func readFileShorter() { const filename = "abc.txt" // Go 函数可以返回两个值 // func ReadFile(filename string) ([]byte, error) if contents, err := ioutil.ReadFile(filename); err != nil { fmt.Println(err) } else { // contents 是 []byte, 用%s 可以打印出来 fmt.Printf("%s", contents) } // if 语句外部不可访问 //fmt.Printf("%s", contents) // 报错 }
从上边可以看出 Go 对文件操作也非常简单。
switch:默认自带break,如果想穿下去执行,使用 fallthrough
// switch 默认自带break,如果想穿下去执行,使用 fallthrough func eval(a, b int, op string) int { var value int switch op { case "+": value = a + b case "-": value = a - b default: panic("unsupport operator" + op) } return value }
五、循环
for:for 的三个组件都可省略,Go 没有 while,用 for 来替代
package main import ( "bufio" "fmt" "os" ) func sum() int { var value int for i := 0; i <= 100; i++ { value += i } return value } // 等同于 while(true) func deadLoop() { for { fmt.Println("this is a deadLoop") } } // Go 没有while,循环全部用 for,for的三个组件都可以省略 func printFile(filename string) { // 打开文件 file, err := os.Open(filename) // 如果出错,结束进程 if err != nil { panic(err) } // 获取读取器 scanner := bufio.NewScanner(file) // 读取:It returns false when the scan stops, either by reaching the end of the input or an error for scanner.Scan() { fmt.Println(scanner.Text()) } }
六、函数
func 函数:
- 可以有多个返回值
- 函数的参数类型可以是 func – 函数式编程
- 支持可变长参数
======================== 可以有多个返回值 ========================= // 函数可返回多个值 // 接收:q, r := div(10, 3) // 如果只用其中一个值,另一个用下划线:q, _ := div(10, 3) func div(a, b int) (int, int) { return a / b, a % b } // use q, _ := div(10, 3) ======================== 函数的参数类型可以是 func ========================= // 可以使用函数作为参数,函数参数与内部参数一样,函数名在前,函数类型在后 // 后续传参,可以使用匿名内部函数,也可以先定义函数再传入 func apply(op func(int, int) int, a,b int) int { return op(a, b) } // use result := apply(func(x int, y int) int { return x + y }, 10,4) ======================== 支持可变长参数 ========================= // 可变长参数 func sum2(nums ...int) int { s := 0 for i := range nums { s += nums[i] } return s } // use sum2(1, 2, 3)
七、指针
Go 只有值传递,引用传递需要借助指针实现,引用传递实际上也是值传递,只是传递的是地址
// 值传递,函数参数拷贝了一份外界的 a, b func swap_by_value(a, b int) { b, a = a, b } // use a, b := 3, 4 swap_by_value(a, b) fmt.Println(*(&a), b) // 3 4 没有实现交换
函数参数拷贝了一份外界的 a, b
// Go 只有值传递,想实现引用传递,使用指针 // *int 代表是指针类型,此时会将外界传入的 &a 拷贝给 这里的a,即这里的 a 拿到的是外界的 a 的地址 // 通过 *a,由于 a 是 &a,这里的 *a 相当于 *(&a) ,即从地址中取值 // 由于函数内部直接操作的是外界的 a,b 的内存地址,所以可以实现引用传递 func swap_by_pointer(a, b *int) { *b, *a = *a, *b } // use swap_by_pointer(&a, &b) fmt.Println(a, b)
*int 代表是指针类型,此时会将外界传入的 &a 拷贝给 这里的a,即这里的 a 拿到的是外界的 a 的地址 通过 *a,由于 a 是 &a,这里的 *a 相当于 *(&a) ,即从地址中取值 由于函数内部直接操作的是外界的 a,b 的内存地址,所以可以实现引用传递