Go语言入门2 – 语法

一、变量定义

四种方式:

  • 完全体: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 的内存地址,所以可以实现引用传递

 

Go语言入门1 – 环境搭建

官方文档:https://golang.org/doc/install,被墙了,所以国内打不开,可以在这个网站下载:https://studygolang.com/dl 。本文以 windows 自动安装版为例:

一、下载:https://studygolang.com/dl/golang/go1.12.4.windows-amd64.msi

安装就不需要说了吧。

➜ ~ go version
go version go1.12.4 windows/amd64

 

二、第一个 Go 程序

使用 GoLand 进行开发,IDE下载地址:https://www.jetbrains.com/go/

package main

import (
   "fmt"
)

func init() {
   fmt.Printf("init\n")
}

func main() {
   fmt.Printf("main\n")
}

运行两种方式:

使用 GoLand 直接 debug / run
执行 go build,然后执行生成的可执行文件 ./helloworld 即可