Go语句基础回顾 | 青训营笔记

126 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记,青训营项目快验收了,明天再对项目做一份笔记,今天回顾一下Go语言基础。

1.Go基础

1.1 运行Go程序

package main
​
import (
    "fmt"
)
​
func main() {
    fmt.Println("hello world")
    var s string = "joker"
    fmt.Println(s)
}

1.2 变量和常量

const 定义常量,var 定义变量

package main
​
import (
    "fmt"
)
​
// 枚举常量
const (
    n1 = "joker"
    n2 = "zehao"
)
​
func main() {
    fmt.Println("hello world")
    var s string = "joker"
    const n3 = 123
    fmt.Println(s,n1,n2,n3)
}

1.3 len(), cap(), unsafe.Sizeof()

len() 字符串长度

cap() 返回的是数组切片分配的空间大小

unsafe.Sizeof() 占多大的空间,为什么string都是16,因为Golang中的sring内部实现由两部分组成,一部分是指向字符串起始地址的指针,另一部分是字符串的长度,两部分各是8字节,所以一共16字节

package main
​
import (
    "fmt"
    "unsafe"
)
​
func main() {
    var word1 = "abc"
    var word2 = "abcde"
    fmt.Println(unsafe.Sizeof(word1)) //16
    fmt.Println(unsafe.Sizeof(word2)) //16
    fmt.Println(len(word1))           //3
    fmt.Println(len(word2))           //5
}

1.4 iota 特殊的常量

iota,特殊常量,可以认为是一个可以被编译器修改的常量

在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1

package main
​
import (
   "fmt"
   "unsafe"
)
​
const (
   n1 = iota
   n2
   n3 = "abc"
   n4
   n5 = iota
   n6
)
​
func main() {
   fmt.Println(n1, n2, n3, n4, n5, n6)
}
​
//输出 :0 1 abc abc 4 5

1.5 运算符(与Java和C语言一样)

1.6 if、switch

package main
​
import "fmt"func main() {
​
    var ck bool = true
    if ck {
        fmt.Println("ck值为true")
    }else {
        fmt.Println("ck值为false")
    }
    
    var grade string = "B"
    var marks int = 90
    switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
    }
    
    
}

switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。

package main
​
import "fmt"func main() {
   var x interface{}
     
   switch i := x.(type) {
      case nil:   
         fmt.Printf(" x 的类型 :%T",i)                
      case int:   
         fmt.Printf("x 是 int 型")                       
      case float64:
         fmt.Printf("x 是 float64 型")           
      case func(int) float64:
         fmt.Printf("x 是 func(int) 型")                      
      case bool, string:
         fmt.Printf("x 是 bool 或 string 型" )       
      default:
         fmt.Printf("未知型")     
   }   
}
​
//输出 : x 的类型 :<nil>

1.7 for、goto

语法

Go语言的For循环有3种形式,只有其中的一种使用分号。

和 C 语言的 for 一样:

for init; condition; post { }

和 C 的 while 一样:

for condition { }

和 C 的 for(;;) 一样:

for { }
  • init: 一般为赋值表达式,给控制变量赋初值;
  • condition: 关系表达式或逻辑表达式,循环控制条件;
  • post: 一般为赋值表达式,给控制变量增量或减量。

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。

numbers := [6]int{1, 2, 3, 5}
for key, value := range numbers {
    fmt.Println(key, value)
}
//输出:
0 1
1 2                     
2 3                     
3 5                     
4 0                     
5 0 

goto语法

goto label
..
.
label: statement

Go 语言的 goto 语句可以无条件地转移到过程中指定的行。

goto语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。

但是,在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。

package main
import "fmt"
func main() {
    for x := 0; x < 10; x++ {
        for y := 0; y < 10; y++ {
            if y == 2 {
                // 跳转到标签
                goto breakHere
            }else{
                fmt.Println(y)
            }
        }
    }
    // 手动返回, 避免执行进入标签
    return
    // 标签
breakHere:fmt.Println("done")
}
​
//输出:
0
1
done

1.8 函数

1.8.1 普通函数

Go 语言函数定义格式如下:

  • parameter list 输入参数可以有多个,要考虑值传递引用传递
  • return_types 返回可以有多个
func function_name( [parameter list] ) [return_types]{
   函数体
}

例如:

func swap(x, y string) (string, string) {
   return y, x
}

引用传递的例子:

func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保持 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

1.8.2 闭包函数

Go 语言支持匿名函数,可作为闭包。创建了函数 ,返回另外一个函数

例子:

package main
​
import "fmt"
​
func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
     return i  
   }
}
​
func main(){
   /* nextNumber 为一个函数,函数 i 为 0 */
   nextNumber := getSequence()  
​
   /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   
   /* 创建新的函数 nextNumber1,并查看结果 */
   nextNumber1 := getSequence()  
   fmt.Println(nextNumber1())
   fmt.Println(nextNumber1())
}
​
//输出:
1
2
3
1
2

1.8.3 方法

Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函数体*/
}
package main
​
import (
   "fmt"  
)
​
/* 定义函数 */
type Circle struct {
  radius float64
}
​
func main() {
  var c1 Circle
  c1.radius = 10.00
  fmt.Println("Area of Circle(c1) = ", c1.getArea())
}
​
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
  //c.radius 即为 Circle 类型对象中的属性
  return 3.14 * c.radius * c.radius
}
​
//输出:Area of Circle(c1) =  314

1.8.4 defer语句

Go语言中的defer语句会将其后面跟随的语句进行延迟处理

在defer所属的函数即将返回时,将延迟处理的语句按照defer定义的顺序逆序执行,即先进后出

package main
​
import "fmt"func main() {
    fmt.Println("开始")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("结束")
}
​
//输出:
开始
结束
3
2
1

1.9 数组

Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:

var variable_name [SIZE] variable_type

1.9.1 一维数组

一维数组的定义方式。数组长度必须是整数且大于 0

var test [10] float32

数组初始化

var test = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

1.9.2 多维数组

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

以下实例声明了三维的整型数组:

var threedim [5][10][4]int

初始化二维数组

a = [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引为 0 */
 {4, 5, 6, 7} ,   /*  第二行索引为 1 */
 {8, 9, 10, 11}   /*  第三行索引为 2 */
}

1.10 指针

1.10.1 什么是指针

一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

1.10.2 Go 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

一个指针变量通常缩写为 ptr。

查看以下实例:

package main
​
import "fmt"func main() {
   var  ptr *int
   fmt.Printf("ptr 的值为 : %v\n", ptr  )
   fmt.Printf("ptr 的值为 : %#v\n", ptr  )
​
}

以上实例输出结果为:

ptr 的值为 : <nil>
ptr 的值为 : (*int)(nil)

空指针判断:

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

1.11 结构体

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

variable_name := structure_variable_type {value1, value2...valuen}

1.11.1 访问结构体成员

如果要访问结构体成员,需要使用点号 (.) 操作符,格式为:"结构体.成员名"。

结构体类型变量使用struct关键字定义,实例如下:

package main
​
import "fmt"
​
type Books struct {
   title string
   author string
   subject string
   book_id int
}
​
func main() {
   var Book1 Books        /* 声明 Book1 为 Books 类型 */
   var Book2 Books        /* 声明 Book2 为 Books 类型 */
​
   /* book 1 描述 */
   Book1.title = "Go"
   Book1.author = "1"
   Book1.subject = "2"
   Book1.book_id = 6495407
​
   /* book 2 描述 */
   Book2.title = "Python"
   Book2.author = "3"
   Book2.subject = "4"
   Book2.book_id = 6495700
​
   /* 打印 Book1 信息 */
   fmt.Printf( "Book 1 title : %s\n", Book1.title)
   fmt.Printf( "Book 1 author : %s\n", Book1.author)
   fmt.Printf( "Book 1 subject : %s\n", Book1.subject)
   fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id)
​
   /* 打印 Book2 信息 */
   fmt.Printf( "Book 2 title : %s\n", Book2.title)
   fmt.Printf( "Book 2 author : %s\n", Book2.author)
   fmt.Printf( "Book 2 subject : %s\n", Book2.subject)
   fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id)
}

输出:

Book 1 title : Go
Book 1 author : 1
Book 1 subject : 2
Book 1 book_id : 6495407
Book 2 title : Python
Book 2 author : 3
Book 2 subject : 4
Book 2 book_id : 6495700

1.12 切片(Slice)

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

使用make() 函数来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

例子:

package main
​
import "fmt"func main() {
    var s = make([]int,2)
    // s := make([]int,2)
    s[0] = 1
    s[1] = 2
    s = append(s,3)
    fmt.Println(s) 
    // [1,2,3]
    
}