浅谈Go基础语法,快速上车

222 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

“基础不牢,地动山摇” 。新接触一门编程语言,基础语法是必须得学扎实了。

前言

今天快速过一遍Go基础语法,先对Go有个简单认识。

主要内容:代码基本结构、变量、常量、数据类型、运算符、流程控制。

下面正式开始每一个知识点的学习吧

1.代码基本结构

学习任何一门编程语言,以经典案例打印 “Hello World”为开始

package main

import "fmt"

func main() {
  fmt.Println("Hello World")
}

简单的一个例子,对代码基本结构先有所认识

  • package:声明包名
  • import:导入标准库或第三方库
  • func:定义函数或方法,注意main函数只能在main下面
  • 其他:变量、常量,得在哪声明和怎么声明呢,那就接着往下学

2.变量

声明

  • 标准声明var name stringvar name = "haha"(后面的类型可省略,go会自动做类型推导)
  • 批量声明var name, address stringvar (name string; age int) 用括号,里边分号可替换为回车多行写
  • 简短声明name := "haha",使用**:=**,注意这种写法只能在函数内使用
package main

import "fmt"

func main() {
  var name string
  
  var (
    name2 string
    age   int
  )
  
  name3 := "haha"

  fmt.Println(name, name2, age, name3)
}

初始化

  • 自动初始化:变量声明后为对应类型零值(int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil)
  • 手动初始化var name string = "haha" name, age := "haha", 18

作用域

  • 全局变量:在函数体外的定义,作用域整个包,甚至在被外部包import后也可使用(注意大写首字母)
  • 局部变量:在函数体内的定义,只能在函数内使用。和全局变量重名时,函数体内局部变量会覆盖掉全局变量,但函数执行完后,全局变量会自动恢复

匿名变量

  • 使用下划线_表示,用于忽略某个值,例如 val, _ := foo(),忽略error;
  • 匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明

3.常量

声明和初始化

  • 单行const a = 1const a, b = 1, 2
  • 多行const(a=1; b=2) 使用括号,里边分号可替换为回车
package main

import "fmt"

func main() {
  const a = 111
  const (
    b = 222
    c = 333
  )

  fmt.Println(a, b, c)
}

注意点

  • 数据类型范围:只能是布尔型、数字型(整数型、浮点型和复数)和字符串型,这些标量类型
  • 常量的值必须是能够在编译时就能够确定的,所以是全局有效

iota

常量计数器,只能在常量表达式中使用。出现时先被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用

//简单例子
const (
  n1 = iota
  n2
  n3
  n4
)
fmt.Println(n1, n2, n3, n4) //0 1 2 3

//注意下面两个例子的区别
const (
  n1 = iota
  n2 = 100
  n3 = iota
  n4
)
fmt.Println(n1, n2, n3, n4) //0 100 2 3 (说明:n3被iota重新赋值,行索引为2,然后下面继续自增)

const (
  n1 = iota
  n2 = 100
  n3
  n4
)
fmt.Println(n1, n2, n3, n4) //0 100 100 100(说明:n3没被重新赋值,则下面的所有值和上一行值相同)

4.数据类型

基本类型

  1. 整型:有符号int、int8、int64、int32、int64;无符号uint、uint8、uint64、uint32、uint64、uintptr
  2. 浮点型:float32、float64、complex32、complex64
  3. 布尔型:true、false(无法参与数值运算,也无法与其他类型进行转换)
  4. 字符串:string,使用双引号,支持转义字符 \n \r \t
  5. 字符:byte(uint8别名)、rune(int32别名),使用单引号

复合类型

arrayslicemapstructchannelfuncinterface

指针类型

pointer:例如*int *string *float32 等等

自定义类型

使用type关键字定义(go类型系统强大之处)

类型转换

go没有隐式类型转换,只能强类型转换,并且两个类型要支持相互转换才能使用

语法:T(表达式)。例子:

func main() {
  a := 10
  b := float32(a)
  fmt.Println(fmt.Sprintf("%T", b), b) //float32 10
}

也可以使用strconv库:支持string类型和其他多个类型的相互转换

5.运算符

常规

  1. 算术运算符:+ - * / %(注意,自增++、自减—,是单独语句,不是运算符)
  2. 关系运算符:== ≠ > ≥ < ≤
  3. 逻辑运算符:&& || !
  4. 位运算符:二进制位操作,包括 & | ^ << >>
  5. 赋值运算符:= += -+ *= /= %= <<= >>= &= |= ^=

其中,<<左移>>右移,代码示例:

fmt.Println(1 << 3)  //8  规则:2的3次方*1
fmt.Println(2 << 3)  //16 规则:2的3次方*2
fmt.Println(2 >> 3)  //0  规则:2除以2的3次方
fmt.Println(16 >> 3) //2  规则:16除以2的3次方

幂运算:使用math库

fmt.Println(math.Pow(2, 4)) //16 含义:2的4次方
fmt.Println(math.Pow10(2))  //100 含义:10的2次方

6.流程控制

分支结构

if else :条件语句不用加括号,多分支选择,并且支持特殊写法

//一层
a := 10
if a > 0 {
  fmt.Println("111")
}
//两层
a := 10
if a > 0 {
  fmt.Println("111")
} else {
  fmt.Println("222")
}
//三层
a := 10
if a > 0 {
  fmt.Println("111")
} else if a > -10 {
  fmt.Println("333")
} else {
  fmt.Println("222")
}
//特殊写法(注意a成为只作用于if内的局部变量)
if a := 10; a > 0 {
  fmt.Println("111")
}

switch case:不需要用break,因为不会自动贯穿,若需要贯穿得用fallthrough;支持单值、多值以及表达式

//常规写法,单个值
finger := 2
switch finger {
case 1:
  fmt.Println("111")
case 2:
  fmt.Println("222")
default:
  fmt.Println("333")
}
//一个分支多个值
switch n := 7; n {
case 1, 3, 5, 7, 9:
  fmt.Println("奇数")
case 2, 4, 6, 8:
  fmt.Println("偶数")
default:
  fmt.Println(n)
}
//分支还可以是表达式
age := 30
switch {
case age < 25:
  fmt.Println("好好学习吧")
case age > 25 && age < 35:
  fmt.Println("好好工作吧")
case age > 60:
  fmt.Println("好好享受吧")
default:
  fmt.Println("活着真好")
}
//fallthrough贯穿到下一个分支
s := "a"
switch {
case s == "a":
  fmt.Println("a")
  fallthrough
case s == "b":
  fmt.Println("b")
case s == "c":
  fmt.Println("c")
default:
  fmt.Println("...")
}
//输出a b

循环结构

for :go没有while和do..while,用for统一替代,用法非常灵活多变

for 初始语句;条件表达式;结束语句{
    循环体语句
}
//常规写法
for i := 0; i < 10; i++ {
  fmt.Println(i)
}
//替代掉while(i<10)语句,可模拟do..while
i := 0
for i < 10 {
  fmt.Println(i)
  i++
}
//替代掉while(true)语句
for {
  循环体语句
}

for range:键值循环遍历数组、切片、字符串、map 及通道(channel)

规律:

  1. 数组、切片、字符串返回索引和值;
  2. map返回键和值;
  3. 通道(channel)只返回通道内的值;
names := []string{"zhangsan", "lisi", "王五"}
for k, v := range names {
  fmt.Println(k, v)
}
//输出:
//0 zhangsan
//1 lisi
//2 王五

其他关键字

goto:跳转到指定标签,慎用,可忽略

break:跳出循环,作用于for switch select

continue:继续下一次循环

总结

对代码基本结构先有所认识,然后对变量常量的声明与初始化多种方式、数据类型、运算符、以及流程控制中的分支结构与循环结构多种用法,都有所学习。

如果本文对你有帮助,欢迎点赞收藏加关注,如果本文有错误的地方,欢迎指出!