go 基础语法

这里是 go 的基本语法大部分内容来源于一门视频课程

学完后再来看手摸手开发一个全栈项目就基本可以直接上手了,项目本身没有什么难度!

变量

go语言定义变量支持类型推导如 a := 2 a 的类型是int

var 关键字

go 复制代码
var a int = 1

:= 只能在函数中定义变量

go 复制代码
a := 1

集中定义变量

go 复制代码
// 1. 在函数中使用  :=
a,b,c := 1,2,"3"

// 2. 在函数外 包内
var (
    a=1
    b=2
    c="3"
)

变量类型

布尔型:bool

只能声明为 true、false

不能接受其他类型的赋值,包括(0, 1),也不支持自动或强制类型转换

字符串:string

整型:(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr

int 类型初始化默认为0

字符型: byte,rune

byte 对应 utf-8

rune 对应 unicode

浮点型: float32, float64

32 与64可相互转换,比较大小时最好先确定比较精度, 再比较大小

复数型:complex64, complex128

分为实部 与虚部 , 64 类型为 float32 128 为64

欧拉公式cmplx.Pow(math.E, 1i * math.Pi) + 1

强制类型转换

go 复制代码
var a, b int = 3, 4
    
// Sqrt 函数接收 float64 返回 float64 所以需要强制类型转换
var c int = int(math.Sqrt(float64(a*a+b*b)))
go 复制代码
package main

import (
    "fmt"
    "strconv"
)

func main() {
    tt1 := []string{"aaaa", "vvvvv", "dddd", "eeeee", "gfgggg"}
    fmt.Println(tt1)

    // 布尔转文本
    // strconv.FormatBool(false)

    // 文字转布尔  true 不能有其他文本  0 false 1 true
    res, _ := strconv.ParseBool("1")
    fmt.Println(res)

    // 格式化 浮点型数字 会四舍五入
    value := strconv.FormatFloat(1.55, 'f', 2, 64)
    fmt.Println(value)

    // 向后追加  int 、 bool
    // strconv.AppendUint()
}

常量的定义

常量的定义使用 const 关键字定义const a = 3 类型推导 a 为 int 类型

枚举的定义

普通枚举

go 复制代码
const(
    cpp = 0
    java = 1
    python = 2
)

自增枚举

go 复制代码
const(
    got = iota
    ptython
    wget
)

// iota 可参与运算
const (
    a = iota * 2
    b
    c
    d
)
fmt.Println(a,b,c,d) // 0 2 4 6

条件语句

if

if 语句 由一个布尔表达式后紧跟一个或多个语句组成。

if else 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。

你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。

go 复制代码
// go 语言的if 判断条件没有 ()
// if 条件中可以声明变量 且在后边语句中使用
if fp, err := os.Open("./textTest/a.txt"); err != nil {
    fmt.Println(err)
} else {
    fmt.Println(fp)
}

switch

switch 语句用于基于不同条件执行不同动作,switch不需要break,也可以直接switch多个条件

go 复制代码
// 一个判断条件
a:=1
switch a {
case 1:
    fmt.Println(a,1)
case 2:
    fmt.Println(a,2)
case 3:
    fmt.Println(a,3)
}

    
// 多个判断条件
var str = "abc"
switch  {
    case a > 10:
        str = "不及格"
    case a > 20:
        str = "及格"
}
return str

循环语句

for

go语言中没有 while 循环,但是 for 可以当作 while 使用

go 复制代码
func forTest()  {
    // 完整 for 循环
    for a:=0; a< 3 ; a++{
        fmt.Println(a)
    }

    file, fileErr := os.Open("./textTest/a.txt")
    if fileErr!=nil {
        panic("打开文件错误")
    }

    scanner := bufio.NewScanner(file)
    // 省略起始条件 递增条件
    for scanner.Scan() {
        // 输出文件的 每行内容
        fmt.Println(scanner.Text())
    }

    // 省略全部条件  是一个死循环  可以通过 break 关键字进行跳出
    for {
        fmt.Println("这是一个死循环")
        break
    }
}

跳出循环

break语句 经常用于中断当前 for 循环或跳出 switch 语句

continue语句 跳过当前循环的剩余语句,然后继续进行下一轮循环。

goto 语句 将控制转移到被标记的语句。(不常用)

Go 语言函数

在go 语言中 函数是一等公民

go语言最少有一个 main 函数也就是主函数(入口函数)

函数声明告诉了编译器函数的名称,返回类型,和参数。

可返回多个值, 返回值类型写在最后面

函数可以作为参数

没有默认参数,可选参数

函数定义

go 语言的函数 由 func 声明

go 复制代码
func funcTest() {}

函数调用

go 复制代码
package main

import "fmt"

func main() {
   // 定义局部变量 var a int = 100 等同于 a := 100
   var a int = 100
   var b int = 200
   var ret int

   // 返回两者相加
   ret = num(a, b)

   fmt.Printf( "两数之和为 : %d\n", ret )
}

函数返回两数之和为
func num(num1, num2 int) int {
   return num1 + num2
}

函数参数

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。 (引用传递需要指针)

函数返回多个值

go 复制代码
// 函数接收两个参数  返回两个参数
func funcTest(a,b int)(c, d int)  {
    c, d = a, b
    // 当为 返回参数定义名字时  return 时 会自动返回对应参数
    return
}

函数接收参数固定 无返回值

go 复制代码
// 函数接收参数固定  无返回值
func funcTest(nums ...int)  {
    // 不定参数时  nums 是一个数组, range 可以用来遍历数组及map
    for i := range nums{
            fmt.Println(nums[i])
    }
}

函数可以作为参数

go 复制代码
// 函数式
func apply(fc func(a,b int)int, a, b int)int  {
    return fc(a,b)
}

// 函数可以作为参数, 接收一个函数返回其相加结果
func funcTest()  {
    fmt.Println(apply(func(a, b int) int {
            return a + b
    },3,4))
}

指针

指针就是一个指向另一个内存地址变量的值

Go指针不能运算

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

go 复制代码
var a, b int= 1, 2
swap2(&a, &b)
fmt.Println(a, b)

func swap2(a , b *int)  {
	*a, *b = *b, *a
}

如何使用指针

定义指针变量。

为指针变量赋值。

访问指针变量中指向地址的值。

获取指针的值 在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。

go 复制代码
package main

import "fmt"

func main() {
   var a int= 20   /* 声明实际变量 */
   var ip *int        /* 声明指针变量 */

   ip = &a  /* 指针变量的存储地址 */

   fmt.Printf("a 变量的地址是: %x\n", &a  )

   /* 指针变量的存储地址 */
   fmt.Printf("ip 变量储存的指针地址: %x\n", ip )

   /* 使用指针访问值 */
   fmt.Printf("*ip 变量的值: %d\n", *ip )
}

// 输出
a 变量的地址是: 20818a220
ip 变量储存的指针地址: 20818a220
*ip 变量的值: 20

go 空指针

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

nil 指针也称为空指针。

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

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

go 复制代码
package main

import "fmt"

func main() {
   var  ptr *int

   fmt.Printf("ptr 的值为 : %x\n", ptr  )
   
   // 通过判断指针变量是否等于 nil 判断其是否是 空指针
   if(ptr != nil)  {
       fmt.Println("ptr 不是空指针")
   }   
   
   if(ptr == nil)  {
       fmt.Println("ptr 是空指针")
   }
}

指针不要操作没有合法指向的内存

go 复制代码
var p * int
p = nil
fmt.Printf("p = %v", p)
*p = 666 //错误, 指针不要操作没有合法指向的内存

new函数的使用

go 复制代码
//p是*int类型,指向int类型
p = new(int)
*p = 666
fmt.Printf("*p = %d", *p)

数组

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。

因为数组的长度是固定的,所以在Go语言中很少直接使用数组。

数组的声明

声明语法 var 数组变量名 [元素数量]Type

数组变量名:数组声明及使用时的变量名。

元素数量:数组的元素数量,可以是一个表达式,但最终通过编译器计算的结果必须是整型数值,元素数量不能含有到运行时才能确认大小的数值。

Type:可以是任意基本类型,包括数组本身,类型为数组本身时,可以实现多维数组。

go 复制代码
// 初始化一个数组变量 默认情况下,数组的每个元素都会被初始化为元素类型对应的零值,对于数字类型来说就是 0
var arr1 [5]int
// 将第一个元素赋值为1 
arr1[0] = 1


// 2 
arr2 := [5]int{1,2,4}
fmt.Println(arr2)

// 3 [...]  表示自动计算数组内 元素个数
arr3 := [...]int{1,12,3,4,5}

多维数组

Go语言中允许使用多维数组,因为数组属于值类型,所以多维数组的所有维度都会在创建时自动初始化零值,多维数组尤其适合管理具有父子关系或者与坐标系相关联的数据。

二维数组是最简单的多维数组,二维数组本质上是由多个一维数组组成的。

声明语法 var array_name [size1][size2]...[sizen] array_type

array_name 为数组的名字, array_type 为数组的类型size1、size2, 等等为数组每一维度的长度。

go 复制代码
// 声明一个 2×2 的二维整型数组
var array [2][2]int
// 设置每个元素的整型值
array[0][0] = 10
array[0][1] = 20
array[1][0] = 30
array[1][1] = 40

比较两个数组是否相等

[10]int[20]int 是不同类型

如果两个数组类型相同(包括数组的长度,数组中元素的类型)的情况下,我们可以直接通过较运算符(==和!=)来判断两个数组是否相等。

只有当两个数组的所有元素都是相等的时候数组才是相等的,不能比较两个类型不同的数组,否则程序将无法完成编译。

go 复制代码
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // 编译错误:无法比较 [2]int == [3]int

数组作为函数参数

数组作函数参数是值传递 如需要引用传递需要使用指针类型数据

go 复制代码
func printArray(arr [5]int)  {
    arr[1] = 100
    for i, v:=range arr {
            fmt.Println(i, v)
    }
}

// 输出  数组作为参数传递到函数后更改元素值  不会更改其原始数组
0 1
1 100
2 4
3 0
4 0

数组遍历 - range

go 复制代码
// range 返回两个参数  i 下标, v 数组元素
//  _ 可用于忽略接收参数  
func printArray2(arr *[5]int)  {
    arr[1] = 100
    for i, v:=range arr {
            fmt.Println(i, v)
    }
}

切片

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

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

切片会动态扩容 每次为上一次的倍数(2的次方)

定义切片

切片不需要说明长度。var identifier []type

make 函数也可以创建切片 var slice1 []type = make([]type, len),简写 slice1 := make([]type, len),其中 []type 表示这是一个什么类型切片。

创建切片

go 复制代码
var arr []int // 初始化一个切片

s1 := []int{1,2,3,4}
fmt.Printf("s1 = ", s1)
 
//make函数,格式(切片类型,长度,容量)
s2 := make([]int, 5, 10)
fmt.Printf("s1 = ", s2)
 
//make函数,格式(切片类型,长度) 长度和容量相同
s3 := make([]int, 5)
fmt.Printf("s3 = ", s3)
 
array := []int{1,2,3,4,5,6,7,8,9}
//操作某个元素,和数组操作方式一样
data := array[1]
fmt.Println("data = ", data)
 
s3 := array[:6]//从0开始,取6个元素,容量也是6,常用
fmt.Println("s3 = ", s3)
 
s4 := array[3:]//从下标为3开始,到结束
fmt.Println("s4 = ", s4)

切片操作

len() 获取切片长度

cap() 获取切片容量

appened(arr1, arr2...) 向且切片添加数据 ...将切片展开

copy() 拷贝切片

切片的删除通过 appened() 来间接实现

slice 可以向后扩展,不可以向前扩展

s[i] 不可以超越 len(s) ,向后扩展不可以超越底层数组 cap(s)

go 复制代码
package main

import "fmt"

func main() {
    // 数组定义 var 数组名称 [数据个数] 数组类型
    // 切片定义 var 数组名称 [] 数组类型
    // 空的切片 长度和容量 均为 0
    // 切片扩容 初始化时确定数据长度  
    // 如果数据不超过 1024 按照 2倍扩容, 超过 1024 按照四分之一扩容
    // 切片扩容 不不确定数据长度 -- 数据长度加二(但一定是二的倍数)

    var sliceArr []int
    fmt.Println(len(sliceArr), "11111111")
    // 在append扩张切片时, 切片的地址可能会发生改变.
    // 如果容量扩充导致输出存储溢出,切片会自动找寻新的空间进行存储,并与之删除之前的空间
    sliceArr = append(sliceArr, 1)
    fmt.Println(sliceArr)

    fmt.Println("操作===================================")
    sliceArr = append(sliceArr, 2, 4, 5, 6, 7, 8, 99, 0)
    sliceArr[0] = 10
    fmt.Println(cap(sliceArr))
    fmt.Println(len(sliceArr))

    var copyArr []int = make([]int, 10)
    // 接受拷贝数据的数据 一定要有足够的容量, 否则无法拷贝 得到空数组
    // 拷贝的两者 互不影响
    copy(copyArr, sliceArr)

    fmt.Println(copyArr, "拷贝的数据")

    /* 创建切片 */
    numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
    printSlice(numbers)

    /* 打印原始切片 */
    fmt.Println("numbers ==", numbers)

    /* 打印子切片从索引1(包含) 到索引4(不包含)*/
    fmt.Println("numbers[1:4] ==", numbers[1:4])

    /* 默认下限为 0*/
    fmt.Println("numbers[:3] ==", numbers[:3])

    /* 默认上限为 len(s)*/
    fmt.Println("numbers[4:] ==", numbers[4:])

    numbers1 := make([]int, 0, 5)
    printSlice(numbers1)

    /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
    number2 := numbers[:2]
    printSlice(number2)

    /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
    number3 := numbers[2:5]
    printSlice(number3)

}

func printSlice(x []int) {
    fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

map 集合

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

需要注意的是map 作为函数参数传递是 是地址传递,函数内更改 map 会影响实参的值 除了slice,map,function的内建类型都可以作为key, Struct类型不包含上次字段,也可以作为key

定义 Map

使用 map 关键字 或者 make 函数。

map 如果不初始化,那么就会创建一个 nil mapnil map 不能用来存放键值对。

go 复制代码
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

遍历map

使用 range 遍历 key 或者遍历 key、value键值对,不保证遍历顺序,如需顺序,需手动对key排序。

go 复制代码
var mMap  map[string]string = map[string]string {
	"a" : "this is a",
	"b" : "this is b",
}

var k, v string
for k, v = range mMap {
    fmt.Printf(k, v)
    fmt.Println("")
}

判断 map 中是否存在指定 key

用 value,ok:=m[key] 来判断是否存在 key

go 复制代码
if v, ok :=mMap["a"]; ok {
    fmt.Printf(v, ok)
} else {
    fmt.Printf("ok is not exist")
}

delete函数

delete函数用于删除集合的元素, 参数为 map 和其对应的 key。

go 复制代码
package main

import "fmt"

func main() {
    /* 创建map */
    countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

    fmt.Println("原始地图")

    /* 打印地图 */
    for country := range countryCapitalMap {
        fmt.Println(country, "首都是", countryCapitalMap[ country ])
    }
    /*删除元素*/
    delete(countryCapitalMap, "France")
    fmt.Println("法国条目被删除")

    fmt.Println("删除元素后地图")

    /*打印地图*/
    for country := range countryCapitalMap {
        fmt.Println(country, "首都是", countryCapitalMap[ country ])
    }
}

// 输出结果为
/*
原始地图
India 首都是 New delhi
France 首都是 Paris
Italy 首都是 Rome
Japan 首都是 Tokyo
法国条目被删除
删除元素后地图
Italy 首都是 Rome
Japan 首都是 Tokyo
India 首都是 New delhi
*/

struct 结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义结构体及访问其成员

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。

如果要访问结构体成员,需要使用点号 . 操作符 结构体.成员名"

go 复制代码
package main

import "fmt"

// 声明一个结构体 Books 结构体名称
type Books struct {
   title string
   author string
   subject string
   book_id int
}


func main() {
    // 初始化一个新的结构体
    fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})

    // 也可以使用 key => value 格式
    fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})

    // 忽略的字段为 0 或 空
   fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
   
   
   // 也可以这样初始化指定字段
   // 通过.可以访问其成员
   var BooksT Books
   BooksT.title = "这是一个书名"
}

结构体作为函数参数

结构体可以和其他类型一样作为函数参数传递

go 复制代码
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 = "www.runoob.com"
   Book1.subject = "Go 语言教程"
   Book1.book_id = 6495407

   /* book 2 描述 */
   Book2.title = "Python 教程"
   Book2.author = "www.runoob.com"
   Book2.subject = "Python 语言教程"
   Book2.book_id = 6495700

   /* 打印 Book1 信息 */
   printBook(Book1)

   /* 打印 Book2 信息 */
   printBook(Book2)
}

func printBook( book Books ) {
   fmt.Printf( "Book title : %s\n", book.title)
   fmt.Printf( "Book author : %s\n", book.author)
   fmt.Printf( "Book subject : %s\n", book.subject)
   fmt.Printf( "Book book_id : %d\n", book.book_id)
}
// 执行结果
Book title : Go 语言
Book author : www.runoob.com
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : www.runoob.com
Book subject : Python 语言教程
Book book_id : 6495700

结构体指针

结构体的指针类似于其他指针变量 var struct_pointer *Books (指针变量可以存储结构体变量的地址)

查看结构体变量地址,可以将 & 符号放置于结构体变量前:struct_pointer = &Book1

指针结构体与普通结构体访问成员方式一致,使用点号 . 操作符 结构体.成员名"

go 复制代码
type node struct {
    value int
    left *node
    right *node
}

var root node
root = node{value:1}
//root := node{value:1}
root.left = &node{value:2}
root.left.right = &node{value:3}
root.right = new(node)

结构体匿名字段

go 复制代码
type Person struct {
    name string
    sex byte
    age int
}
 
 
type Student struct {
    Person //只有类型,没有名字,匿名字段,继承了Person的成员
    id int
}
 
func main() {
    var s1 Student = Student{
        Person: Person{},
        id:     0,
    }
    fmt.Print(s1)

    //自动推导类型
    s2 := Student{
        Person: Person{},
        id:     0,
    }
    fmt.Println(s2)
}

往期文章

相关推荐
linkingvision21 分钟前
Chrome 136 H265 WebRTC 支持 正式版本已包含
前端·chrome·webrtc
光影少年1 小时前
新手学编程前端好还是后端
前端·后端
why1511 小时前
百度网盘golang实习面经
开发语言·后端·golang
Deepsleep.1 小时前
CSS Transition入门指南
前端·css
牧杉-惊蛰2 小时前
修改输入框选择框颜色
前端·html
Catfood_Eason2 小时前
XML简介
xml·java·前端
黄同学real3 小时前
ES6 知识点整理
前端·javascript·es6
JQLvopkk3 小时前
c#读取txt指定行
java·前端·c#
hac13224 小时前
SpringBoot多环境配置
java·spring boot·后端
姜太小白4 小时前
【前端】CSS实现div双背景图叠加
前端·css