必知的Go基础语法 | 青训营笔记

87 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

image.png

笔记内容概述

  • 变量
  • if-else
  • for循环
  • switch分支结构
  • 数组
  • map
  • rang
  • 函数
  • 指针
  • 结构体
  • 结构体方法
  • 错误处理

变量

go语言是一门强类型语言,每一个变量都是有自己的变量类型,常见的变量类型有string字符型,int整数型,float浮点型(float(64),float(32)),bool布尔型。

变量声明

go可以通过 var 变量名 = value 的形式来声明变量,例如 var str = "hello,world!",了解过JavaScript的小伙伴是不是感觉和JavaScript的变量生命方式一样呢?但要注意,go是一门强类型语言,而JavaScript是一门弱类型语言,只不过go会自动进行类型推断。go也可以通过 var 变量名 变量类型 = value来声明变量,与大多数编程语言使用 变量类型 name = value 的方式不同,go的变量类型放在变量名后面,例如var str string = "hello,world!,也可以使用 变量名 := value的形式声明变量,例如str := "hello,world!"

string是go的内置变量类型,和java一样支持使用 "+" 号进行字符串的拼接,如

package main

import "fmt"

func main() {
   var str1 = "hello,"
   var str2 = "world!"
   var str = str1 + str2
   fmt.Println(str)
}

注意在编写go的每条语句结束后不需要使用 ";" 进行标识,直接换行即可(go的正式语法是使用";"表示语句的结束的,但这些 ";" 并不会在源码中出现),go的词法分析器会自动插入分号

常量声明

go的常量声明使用关键字const,如 const aint int = 10,const aint = 10都是可以的,go的常量并没有确切的类型,会在编译期间根据上下文自动推导类型

if-else

go的if-else语句和其他大多数编程语言类似,只不过go的if-else语句后面不需要使用括号,且循环体必须使用大括号包围(java,c++当循环体只有一条语句时可以不使用大括号包围循环体)例如:

package main

import "fmt"

func main() {
   var a = 10
   if a == 10 {
      fmt.Println("a是", a)
      a = 20
   }
   if a == 20 {
      fmt.Println("a现在是", a)
   } else {
      fmt.Println("a既不是10也不是20")
   }
}

for循坏

go中没有while循环,do-while循环,只有唯一的for循环。

for的条件参数什么都不写的话,就是一个死循环,例:

package main

import "fmt"

func main() {
   for {
      fmt.Print("go")
   }
}

for的经典三段句,例:

package main

import "fmt"

func main() {
   for i := 0; i < 10; i++ {
      fmt.Print(i, " ")
   }
}

for使用单条件判断,例:

package main

import "fmt"

func main() {
   var i int = 0
   for i < 10 {
      fmt.Print(i, " ")
      i++
   }
}

go的for循环中也可以使用break跳出循环,continue继续执行循环。

switch分支结构

go的switch分支结构非常强大,甚至可以取代if-else语句,其后面也不需要使用"()"包围变量,switch支持任意变量类型进行判断,如string,int,float,bool,和结构体变量,当不使用任何变量时,会判断每个分支,和java,c++不同的是,go的switch分支得到一个case满足后就会跳出整个switch(java,c++如果不在case语句最后加上break的话会继续执行后面的case)。

package main

import (
   "fmt"
)

func main() {
   a := 10
   switch a {
   case 5:
      fmt.Println("a为5")
   case 10:
      fmt.Println("a为10")
   default:
      fmt.Println("no more")
   }

   switch {
   case a != 20:
      fmt.Println("a不是20")
   default:
      fmt.Println("unknown")
   }
}

数组

go的数组的声明方式为 var 数组名 []数组类型 = value,如果在声明时就对数组进行初始化时需要在大括号前面使用[]数组类型,如:var aint []int = []int{1,2,3,4},数组是在地址上连续且长度不可变,但可使用切片slice来替换数组,slice是长度可变的数组,它实际上就是一个指向数组的指针和一个数组长度,当数组需要变长时,实际上是通过把指针指向一个更长的长度不可变的数组,并把原来数组的值也一并拷贝过来来实现变长数组。

package main

import "fmt"

func main() {
   var aint []int = []int{1, 2, 3, 4}
   fmt.Println(aint)
   aslice := make([]string, 2)
   aslice = []string{"A", "B", "C", "D", "E"}
   fmt.Println(aslice)
}

map

map也是通过make来声明的,make(map[键类型]值类型),如:amap:=make(map[string]int)声明了一个key为string类型,value为int类型的map,map中的元素都是无序的,通过map[key] = value的方式可以给key赋值,通过map[key]的方式可以读取该key对应的值,通过delete(map名字,key)可以删除指定的key-value对。

package main

import "fmt"

func main() {
   amap := make(map[string]int)
   amap["A"] = 1
   amap["B"] = 2
   fmt.Println(amap)
   v, e := amap["A"]
   fmt.Println(v, e)
   delete(amap, "A")
   fmt.Println(amap)
}

rang

go的rang可以用来给数组或map进行遍历,rang会返回两个值,对于数组第一个值是索引,第二个值是该索引对应的,对map来说第一个值是key,第二个值是key对应的value

package main

import (
   "fmt"
)

func main() {
   tarray := []int{1, 23, 4, 5, 6}
   for k, v := range tarray {
      fmt.Println(k, ":", v)
   }

   amap := make(map[int]string)
   amap[1] = "a"
   amap[2] = "b"
   amap[3] = "c"
   for k, v := range amap {
      fmt.Println(k, ":", v)
   }
}

函数

go函数使用func关键字来声明,声明方式:func 函数名(参数) 返回值类型,例如:func sum(a int,b int) int,如果不声明任何返回值类型,则认为该函数不需要返回值,go函数支持返回多个函数。

package main

import (
   "fmt"
)

func main() {
   fmt.Println(sum(10, 5))
   a, b := returnMore(10, 5)
   fmt.Println(a, b)
}

func sum(a int, b int) int {
   return a + b
}

func returnMore(a int, b int) (c, d int) {
   return a, b
}

指针

声明方式: var 指针名 *指针类型,如:var a *int申请了一个int类型的指针,go的指针的功能非常受限,一般用来在函数中作为参数,因为函数的参数都是拷贝的副本,对参数进行修改并不会对原值有影响,由于使用指针避免了参数的拷贝,所以对性能有一定的帮助。

package main

import "fmt"

func main() {
   a, b := 10, 5
   fmt.Println(a, b)
   exchange(&a, &b)
   fmt.Println(a, b)

}
func exchange(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

传递指针作为参数时,如果即将传递的参数并不是指针时,需要使用 &取地址符 进行传参,这和c++一样。

结构体

go结构体的定义方式为:tyep 结构体名称 struct{},通过 " . "来访问结构体里的变量或方法,或对变量进行赋值初始化

package main

import "fmt"

func main() {
   per := person{}
   per.name = "asjun"
   per.age = 21
   fmt.Println(per)
}

type person struct {
   name string
   age  int
}

在声明时也可以通过{属性名:值}的方式进行初始化,多个属性用" , "分隔。

结构体方法

go虽然没有类的概念来定义类方法,但是通过结构体方法也能实现相同的功能。

go结构体方法定义方式:func (要定义方法的结构体名称) 方法名(参数) 返回值,和普通方法很像,只不过在func关键字后面指明一个结构体的名称,如果紧跟着的是结构体的变量,就可以在该方法内操控这个变量,通过设置结构体指针变量就可以在方法内获得指向自身的指针

package main

import "fmt"

func main() {
	pson := person{}
	pson.aMethod()
	pson.setNameAndAge("asjun", 21)
	fmt.Println(pson)
	fmt.Println(pson.matchName("asjun"))
}

type person struct {
	name string
	age  int
}

func (person) aMethod() {
	fmt.Println("这是结构体方法")
}

func (pson person) matchName(name string) bool {
	return pson.name == name
}

func (this *person) setNameAndAge(name string, age int) {
	this.name = name
	this.age = age
}

错误处理

go通过返回多参数的特性来直接返回错误,go的错误处理相比java等非常的方便,也更容易定位到发生错误的代码。如除0错误:

package main

import (
   "errors"
   "fmt"
)

func main() {
   v, e := division(10, 0)
   if e != nil {
      fmt.Println(e)
      return
   }
   fmt.Println(*v)
}

func division(a int, b int) (result *int, err error) {
   if b == 0 {
      return nil, errors.New("除0错误")
   }
   var res int = a / b
   return &res, nil
}

以上就是go的必知基础知识,可能还有很多知识点并没有记下,小伙伴们可自行百度,或在评论区留言。

共勉!