Golang基础

Go语言

1. Go语言简介

1.1 来历和发展目标

  • 以C为原型。

  • 静态语言的安全性和高效性与动态语言的易开发性进行有机结合。(C+Python)

  • 用更少的代码,更短的编译时间,创建运行更快的程序,享受更多的乐趣(吉祥物是一个囊地鼠(Gopher)表示发展迅速)

1.2 特点

  • 静态编译

  • 强类型语言

  • 函数具有多返回值

  • 自带GC

  • 简单的思想,没有继承、多态、类等

  • 丰富的库和详细的文档

  • 语法层支持并发,和拥有同步并发的channel类型

1.3 语言的用途

  • 应用于服务器的系统编程语言

  • Go 语言同时也是一门可以用于实现一般目标的语言,例如对于文本的处理,前端展现等

2. 安装

2.1 Go环境变量

  • Go开发环境依赖于一些操作系统环境变量

    • ** G O R O O T ∗ ∗ 表 示 G o 在 你 的 电 脑 上 的 安 装 位 置 , 它 的 值 一 般 都 是 ‘ GOROOT** 表示 Go 在你的电脑上的安装位置,它的值一般都是 ` GOROOTGoHOME/go`,也可以安装在别的地方。
    • $GOARCH 表示目标机器的处理器架构
    • $GOOS 表示目标机器的操作系统
    • ** G O B I N ∗ ∗ 表 示 编 译 器 和 链 接 器 的 安 装 位 置 , 默 认 是 ‘ GOBIN** 表示编译器和链接器的安装位置,默认是 ` GOBINGOROOT/bin`,

2.2 在Windows安装

  1. 下载Go的安装包 下载地址
  2. 安装后,配置环境变量$GOPATH 即可使用

2.3 Goland配置

GoLand安装及环境配置

2.3.1 项目文件
  • image-20210812144810401 - **bin存放编译后的可执行文件,** - **pkg存放编译后的包文件** - **scr 存放项目源文件(包和包中的源文件)**
    • 一般,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
    • 对于src目录,存放源文件,Go中源文件以包(package)的形式组织。通常,新建一个包就在src目录中新建一个文件夹。

3. Go程序的一般结构

package main // 声明本文件的包名

import "fmt" // 导入包
//导入fmt库(用于输出)

func main() { // {必须放在函数头一行
    fmt.Println("Hello,World!")	
}
  • 程序的代码通过语句来实现结构化。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。

4. 文件名、关键字与标识符

4.1 文件名

  • .go

  • 文件名一般小写

  • _对多个部分进行分隔 scanner_test.go

4.2 标识符

  • Go区分大小写

  • 无效标识符

    • 1ab (以数字开头)
    • case (Go语言的关键字)
    • a+b (运算符是不允许的)
  • _ 特殊标识符

    • 空白标识符
    • 任何类型都可以赋值给它
    • 任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用

4.3 关键字

  • 25个关键字和保留字
breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

5. 数据类型

5.1 基本类型

  • 数字类型

    • 整型:int8, int16, int, int32, int64;
    • 无符号整型:uint8, uint16, uint, uint32, uint64
    • 浮点型:float32, float64
  • 布尔类型:bool

  • 字符串类型: string

  • 数组类型:[]int

  • 切片类型:make([]int, 10)

  • map类型:make(map[string] string)

  • 支持复数

  • Go输出可以使用格式化输出,利用%d,%s,%p,%v

6. 常量与变量

6.1 常量

  • 常量使用关键字const定义,存储不会改变的数据。
  • 存储在常量中的数据类型只可以是布尔型,数字型(整数型,浮点型,复数)和字符串型
  • 常量的定义格式:const identifier [type] = value 例如:const Pi = 3.14 (类型可以省略)

6.2 变量

  • 一般形式:var identifier type

  • 分解写法

    var (
    	a int
    	b bool
    	str string
    )
    //
    // 函数内部的写法 :=
    func f1(){
      :=
    a := 1
    b := "eswinESWIN"
    }
    
    • 声明后的默认值int 为0,float 为0.1,bool为false,string为空字符串,指针为nil.
  • 变量命名遵循驼峰规则

7. 控制结构

Go语言具有以下控制结构:

  • if-else 结构

  • switch 结构

  • for (range) 结构

  • select 结构 (用于channel的选择)


  • if语句用法和C完全一样

    if x>0{
    	return y
    }else{
    	return x
    }
    
    • if 可以包含一个初始化语句(如:给一个变量赋值)。这种写法具有固定的格式(在初始化语句后方必须加上分号)
    if x:=6;x>0{
    	return y
    }else{
    	return x
    }
    
  • switch结构

    switch varA {
    	case val1:
    		...
    	case val2:
    		...
    	default:
    		...
    }
    
    • varA的类型必须 和 val1 、val2相同,例如 varA 是数字, 那么 val1,val2不能是bool值,或者结果为bool的表达式 varA默认true

    • 可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3

    • 不需要break来结束

    • 程序不会自动地执行下一个分支的代码,若想继续执行后续分支的代码,可以使用fallthrough关键字。

  • for 结构

    • Go中重复执行只有for 结构
    1. 基于计算器的迭代
for 初始化语句; 条件语句; 修饰语句 {}

​ 2. 基于条件判断的迭代:for 条件语句{}

  • for range结构

    • for ix, val := range coll {}
  • breakcontinue (和C一样)

    • break语句的作用结果是跳过整个代码块,执行后续的代码。
    • 关键字continue忽略剩余的循环体而进入下一次循环的过程。

8. 函数

8.1 函数介绍

  • Go是编译型语言,对函数的编写顺序没有要求,一般main函数在前

  • 函数一般形式

    func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
    
  • 函数可以利用return0个或者多个返回值,并且可以返回函数

    • 这种多返回值一般用于判断某个函数是否执行成功(true/false)或与其它返回值一同返回错误消息。
  • 函数调用

    pack1.Function(arg1,arg2,...,argn)
    
    • Functionpack1包里面的一个函数,括号里面是实参,实参将会被复制给形参。
  • Go默认使用按值传递来传递参数,也就是传递参数的副本。

    • 若想修改实参的值,需要传递实参的地址。(&+ 变量名)(引用传递)

9. defer

  • 关键字defer 允许我们推迟到函数返回之前(或任意位置执行return语句之后),才执行某个语句或函数
func a() {
	i := 0
	defer fmt.Println(i) // 先得到i=0的值,最后在return之后输出
	i++
	fmt.Println(i) 
	return
}
// 函数会输出 1 
//			 0
  • 当有多个defer 行为被注册时,它们会以逆序执行(后进先出)
func a() {
	i := 0
	defer fmt.Println(i) a
	i++
	fmt.Println(i)
	defer fmt.Println(i) b
	return
}
// 函数会输出 1 fmt.Println(i)       
//			 1 defer fmt.Println(i) b
//           0 defer fmt.Println(i) a
func f() {
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)
	}
}
//输出:4 3 2 1 0。
  • 关键字defer的用法类似于面向对象编程语言Java的 finally语句块,它一般用于释放某些已分配的资源。(关闭文件流,解锁资源,关闭数据库连接)

10. 匿名函数(闭包)

  • 匿名函数 (没有函数名)
    • f1 := func(x, y int) int { return x + y } 将函数赋值给变量f1
      • 通过变量来对函数进行调用 f1(3,4)
      • 匿名函数可以被赋值给变量并作为值使用
    • func(x, y int) int { return x + y } (3, 4)
      • 直接对匿名函数进行调用

11. 数组与切片

11.1 数组

  • 长度固定、元素类型一致的序列

    • 数组为值类型,在传参的时候会发生整个数组拷贝(并不是数组第一个元素的地址拷贝)。
    • 长度为0的数组不占用内存空间
    • 元素的数目,长度或数组的大小必须是固定的,并且在声明该数组时就给出。(编译时需要根据数组长度以便分配内存
  • 定义和初始化

    func main() {
    	var a [3]int //基本定义
    	// ... 指定数组长度为最大索引+1 
    	var b = [...]int{1, 2, 3}//顺序初始化
    
        var c = [...]int{0:1,1:3}//索引初始化(key-value)
    	var d = [...]int{0:1,1:3,10:12,13,14}//混合初始化,既使用顺序初始化又使用索引初始化
    	)
    }
    
    
    • 数组的地址可以通过数组名获取: &a

      func main() {
          var a = [...]int{1, 2, 3} // a 是一个数组
          var b = &a                // b 是指向数组的指针
          fmt.Println(a[0], a[1])   // 打印数组的前2个元素
          fmt.Println(b[0], b[1])   // 通过数组指针访问数组元素的方式和数组类似
      }
      
11.1.1 将数组传递给函数
  • 直接传递数组

    func sum(a [5]int) int { //形参是数组
    	s := 0
    	for i := 0; i < len(a); i++ {
    		s += a[i]
    	}
    	return s
    }
    
    func main() {
    	var arr = [5]int{0, 1, 2, 3, 4}
    	sum(arr) // 传递数组
    }
    
    • 把一个大数组传递给函数会消耗很多内存,有两种方法可以避免这种现象
      • 传递数组的指针
      • 使用数组的切片
  • 传引用

    func sum(a *[5]int) int {
    	s := 0
    	for i := 0; i < len(a); i++ {
    		s += a[i]
    	}
    	return s
    }
    
    func main() {
    	var arr = [5]int{0, 1, 2, 3, 4}
    	sum(&arr)// 传递数组的地址
    }
    
    
  • 传切片

    func sum(a []int) int { // 形参为切片
    	s := 0
    	for i := 0; i < len(a); i++ {
    		s += a[i]
    	}
    	return s
    }
    
    func main() {
    	// 1. 将数组转成切片进行传递
    	var arr = [5]int{0, 1, 2, 3, 4}
    	sum(arr[:])
        // 2. 直接定义切片进行传递
    	var arr = []int{0,1,2,3,4}
        sum(arr)
    }
    

11.2 切片

  • 概念

    • 切片(slice)是对数组一个连续片段的引用(该数组称为相关数组,通常是匿名的。)
      • 这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集
    • 切片是一个引用类型(类似于C中的数组类型)
    • 切片是一个长度可变的数组
    • cap()函数可以测量切片最长可以达到多少(切片的容量):
      • 等于切片的长度+ 数组除切片以外的长度(前面的不算,只能往后扩容)。
      • 如果s是一个切片,cap(s) 就是从s[0]到数组末尾的数组长度,切片的长度永远不会超过它的容量。0 <= len(s) <= cap(s)
  • 优点

    • 切片是引用,因此不需要额外的内存
  • 声明格式

    • var identifier []type(不需要说明长度)。
  • 初始化格式

    • var slice1 []type = arr1[start:end]

      • 改变start和end的值可以得到不同的切片

      • 其他形式 slice1 = &arr1


  • 切片在内存中的组织方式:

    • 3 个域的结构体:指向相关数组的指针,切片长度以及切片容量。

    • 下图给出了一个长度为2, 容量为4的切片y

      • y[0] = 3y[1] = 5
      • 切片 y[0:4] 由 元素 3,5,7 和 11 组成。
11.2.1 切片的复制和追加
  • 复制:copy()函数,返回复制的个数(int

  • 追加:append()函数(常用作扩容)

    package main
    import "fmt"
    
    func main() {
    	slFrom := []int{1, 2, 3}
    	slTo := make([]int, 10)// 常用作生成切片
        // make([]type,len,caps) 
    	// caps默认len,可以省略
    	n := copy(slTo, slFrom)
    	fmt.Println(slTo)
    	fmt.Printf("Copied %d elements\n", n) // n == 3
    
    	sl3 := []int{1, 2, 3}
    	sl3 = append(sl3, 4, 5, 6)
    	fmt.Println(sl3)
    }
    
  • append([]T, X...T) []T 其中append方法将0个或多个具有相同s的元素追加到切片后面并且返回新的切片

  • 追加的元素必须和原切片的元素同类型

    • 如果s的容量足够,增加后的切片和原切片指向同一地址(一个相关数组)。
    • 如果s的容量不足以存储新增元素,append会分配新的切片保证存储(不同数组)。
package main

import "fmt"

func main() {
	sl3 := make([]int, 10, 15)// 最大容量15
	fmt.Printf("%p\n", sl3)
	sl3 = append(sl3, 4, 5, 6)// 没超过15
	fmt.Println(sl3)
	fmt.Printf("%p\n", sl3) 
}
// 输出
//0xc00001c100
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc00001c100
package main

import "fmt"

func main() {
	sl3 := make([]int, 10, 12) // 最大容量12
	fmt.Printf("%p\n", sl3)
	sl3 = append(sl3, 4, 5, 6) // 超过12
	fmt.Println(sl3)
	fmt.Printf("%p\n", sl3) // 指向不同的地址
}
// 输出
//0xc00001e0c0
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc000112000

)。

package main

import "fmt"

func main() {
	sl3 := make([]int, 10, 15)// 最大容量15
	fmt.Printf("%p\n", sl3)
	sl3 = append(sl3, 4, 5, 6)// 没超过15
	fmt.Println(sl3)
	fmt.Printf("%p\n", sl3) 
}
// 输出
//0xc00001c100
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc00001c100
package main

import "fmt"

func main() {
	sl3 := make([]int, 10, 12) // 最大容量12
	fmt.Printf("%p\n", sl3)
	sl3 = append(sl3, 4, 5, 6) // 超过12
	fmt.Println(sl3)
	fmt.Printf("%p\n", sl3) // 指向不同的地址
}
// 输出
//0xc00001e0c0
//[0 0 0 0 0 0 0 0 0 0 4 5 6]
//0xc000112000
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值