go基础回顾|青训营笔记

86 阅读10分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记

1 前言

在对go 语言有了一段时间的学习经历之后,我越发觉得我已经到了开始学习go 的阶段了,所以重新回看青训营视频,回炉重造。

2 写Go语言需要注意的地方

Go语言语句后边不要带分号(它会在每行后自动加分号) Go语言应用程序的执行入口是main()函数 Go语言严格区分大小写 由于Go编译器是一行行进行编译的,因此一行就写一条语句,不能把多条语句写在同一行 Go语言定义的变量或者import的包如果没有使用到,代码不能编译通过

2.1 Go语言编译执行和直接run的区别

  1. 如果先编译再执行,那么编译后的二进制可执行文件可以拷贝到没有go开发环境的机器上,仍然可以运行。
  2. 如果直接run源代码,那么如果这段代码拷贝到其它没有go开发环境的机器上,是无法正常执行的
  3. 在编译时,编译器会将程序依赖的库文件包含在可执行文件中,所以编译后的可执行文件变大了许多

2.2 Go语言的特点

Go语言保证了既能达到静态编译语言的安全和性能,又达到了动态语言开发和维护的高效率,使用一个表达式来形容Go语言:Go=C + Python

  1. Go从C语言中集成了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指针等等,也保留了和C语言一样的编译执行方式及弱化的指针。
  2. 引入了包的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在。
  3. 垃圾回收机制gc,内存自动回收,不需要开发人员管理。
  4. 天然支持并发。

(1)从语言层面支持并发,实现简单。 (2)goroutine,轻量级线程,可实现大并发处理,高效利用多核。 (3)基于CPS并发模型(Communicating Sequential Processes)实现。

  1. 吸收了管道通信机制,形成Go语言特有的管道channel。
  2. 函数可返回多个值。
  3. 支持切片、延时执行defer等等。

2.3 Linux下配置Go环境变量

vim /etc/profile
export GOROOT=/opt/go
export PATH=$GOROOT/bin:$PATH
export GOPATH=$HOME/goprojects/

SDK: 软件开发工具包,首先要配置好SDK GOROOT: 指定SDK的安装目录 GOPATH: GO项目的目录 Path: 指定SDK目录下边的bin目录

单行注释:

//尽量用行注释来注释代码块
//Ctrl +  / 快速注释

块注释:

/*
        ...
*/
// 块注释中不允许有块注释嵌套

2.4 随记

为什么需要变量 变量是程序的基本组成单位,其相当于内存中的一个数据存储空间的表示,我们通过变量名可以访问到变量(值)

变量的基本使用:
1、声明变量(定义变量)
2、赋值
3、使用

gofmt -w xxx.go 可以自动对齐代码

3 go_code

3.1 Variable(变量)

3.1.1 int

package main

// import "fmt"
// import "unsafe"

import (
	"fmt"
	"unsafe"
)

func main()  {
	// 有符号整数(int)的范围
	// int8		1bytes	-128~127
	// int16 	2bytes	-2^15~2^15-1
	// int32	4bytes	-2^31~2^31-1
	// ...
	var y int8 = 127 //当y为128时就会报错
	fmt.Println("y=", y)

	// 无符号整数(uint)的范围
	// uint8 	1bytes 	0~255
	// uint16 	2bytes	0~2^15-1
	// uint32 	4bytes	0~2^32-1
	// ...
	var h uint8 = 255
	fmt.Println("h=", h)

	// rune 有符号整数类型 类似int32 表示一个Unicode码
	// byte 无符号整数类型 与uint8等价 当要存储字符时选用byte
	var a int = 9000
	var b uint = 1 
	var c byte = 255
	var d rune = 100
	fmt.Println("a=", a, "b=", b, "c=", c, "d=", d)

	// 查看某个变量的数据类型 fmt.Printf()
	fmt.Printf("a 的类型 %T\n", a)

	// 查看某个变量的字节大小
	fmt.Printf("a 的类型 %T,占用的字节数是 %d", a, unsafe.Sizeof(a))

	// 在Golang中使用变量的时候,遵守保小不保大的原则
	// 即:在保证程序正确运行下,尽量使用占用空间小的数据类型,如年纪age可以使用byte类型
	var age byte = 20
	fmt.Println("age=",age)

	// bit:计算机中的最小存储单位。byte:计算机中基本存储单元 1byte = 8bit
}

3.1.2 float
package main
import (
	"fmt"
)


func main()  {
	var price float32 = 88.88
	fmt.Println("price is ", price)

	// go语言中小数类型分为两大类,单精度float32,双精度float64
	// float32	4bytes
	// float64	8bytes
	// 浮点数=符号位+指数位+尾数位(浮点数都是有符号位的)

	var num1 float32 = -0.00001
	var num2 float64 = -78098238.08
	fmt.Println("num1=",num1, "num2=", num2)
	fmt.Println("num1=",num1, "num2=", num2)

	// 尾数部分可能丢失,造成精度丢失,同样记录一个-123.00000901双精度要比单精度更精确

	// Golang的浮点类型默认声明为float64
	var num3 = 2.2
	fmt.Printf("num3的数据类型为%T\n",num3)

	// 十进制数形式:
	num4 := 5.12
	num5 := .123 // =>0.123
	fmt.Println("num4=",num4, "num5=", num5)
	fmt.Println("num4=",num4, "num5=", num5)

	// 科学计数法形式
	num6 :=5.12345e2  // 5.12345 * 10的2次方	512.345
	num7 :=5.12345E2  // 5.12345 * 10的2次方	512.345
	num8 :=5.12345E-2  // 5.12345 / 10的2次方 0.0512345
	fmt.Println("num6=",num6, "num7=", num7, "num8", num8)

	// 推荐使用float64
}
3.1.3 string

package main
import (
	"fmt"
)

func main()  {
	var address string = "我爱你中国 12345 hello world"
	fmt.Println(address)

	// 字符串一旦赋值内容就不能修改:在go中字符串是不可变的
	// var str = "hello"
	// str[0] = 'a'  这样就会报错

	// 字符串有两种表示形式
	// (1)双引号,会识别转义字符
	// (2)反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果
	str2 := "abc\nabc"
	fmt.Println(str2)

	str3 := `
	afd
	afdf
	af
	asf
	afds
	`
	fmt.Println(str3)

	// 字符串拼接 +
	var str4 = "hello" + "world"
	str4 += " haha!"
	fmt.Println(str4)

	// 当一个拼接的操作很长时,可以分行写,拼接符留在上一行
	var str5 = "hello" + "world" +
	"hello" +
	"world"
	fmt.Println(str5)
}
3.1.4 byte
package main

import (
	"fmt"
)

// 字符串就是一串固定长度的字符连接起来的字符序列
func main()  {
	// Golang中没有专门的字符类型,如果要存储单个(字母),一般使用byte来保存
	// Golang的字符串是由字节组成的
	var c1 byte = 'a'
	var c2 byte = '0'	// 字符的0
	fmt.Println("c1=", c1, "c2=", c2)  // c1= 97 c2= 48 会输出ASCII码值
	fmt.Printf("c1=%c c2=%c\n", c1, c2)  // c1=a c2=0

	// var c3 byte = '京'  // 结果会溢出 overflow
	var c3 int = '京'
	fmt.Printf("c3=%c c3对应的码值=%d\n", c3, c3)  // c3=京 c3对应的码值=20140

	// 如果我们保存的字符在ASCII表中,比如[0-9,a-z,A-Z]可以直接保存到byte
	// 如果我们保存的字符对应码值大于255,这时我们可以考虑使用int类型保存
	// 如果我们需要按照字符的方式输出,需要格式化输出,即 fmt.Printf("%c")

	// utf-8编码包含了ASCII编码,英文字母1个字节,汉字3个字节

	// 在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值

	var c4 int = 22269  // 22269 -> '国'
	fmt.Printf("c4=%c\n", c4)

	// 由于字符类型本质是一个整数,所以其也是可以运算的,运算的时候是按照码值运算
	var n1 = 10 + 'a'
	fmt.Println("n1=", n1)  //n1= 107
}

// 字符型存储到计算机中,需要将字符对应的码值(整数)找出来
// 存储:字符-->对应码值-->二进制-->存储
// 读取:二进制-->码值-->字符-->读取
// Go语言的编码都统一成了utf-8,所以不会有乱码出现
3.1.5 bool
package main

import (
	"fmt"
	"unsafe"
)

func main()  {
	var b = false
	fmt.Println("b=",b)

	// bool类型占用的存储空间是1个字节
	fmt.Println("b 的占用空间 =", unsafe.Sizeof(b))

	// 布尔类型也叫bool类型,只允许其取值true or false
	// 常用在循环语句中
	// bool值默认是false
}
3.1.6 Var Transform(类型转换)
3.1.6.1 string转基本数据类型
package main

import (
	"fmt"
	"strconv"
)

func main()  {
	var str string = "true"
	var b bool

	// 转bool
	// strconv.ParseBool会返回两个值,第一个值是布尔值,第二个值是error值
	// 我们只想获取到布尔值,对err不关心,所以可以用下划线 _ 来忽略掉err
	b , _ = strconv.ParseBool(str)
	fmt.Printf("b type %T b=%v\n",b, b)
	// 输出结果:b type bool b=true

	// 转int
	var str2 string = "123456"
	var n1 int64
	var n2 int
	n1, _ = strconv.ParseInt(str2, 10, 64)
	n2 = int(n1)
	fmt.Printf("n1 type %T n1=%v\n", n1, n1)
	fmt.Printf("n2 type %T n2=%v\n", n2, n2)

	var str3 string = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("f1 type %T f1=%v\n",f1 ,f1)

	// 在String类型转成基本数据类型时,要确保String类型能够转成有效的数据
	// 比如我们可以把"123"转成一个整数,但是不能把“hello”转为一个整数,如果这样Golang会将其转为0
	// 如果n3没有转换成功,则会转成默认值0
	var str4 string = "hello"
	var n4 int64 = 11
	n4, _ = strconv.ParseInt(str4, 10, 64)
	fmt.Printf("n4 type %T n4=%v\n", n4, n4)
	// 输出结果:n4 type int64 n4=0

	// 字符串转换为bool的情况会转为默认false
	var str5 string = "hellook"
	var b2 bool = true
	b2 , _ = strconv.ParseBool(str5)
	fmt.Printf("b2 type %T b2=%v\n",b2, b2)
	// 输出信息:b2 type bool b2=false
}
3.1.6.2 基本数据类型转string

// 在开发中,经常需要将基本数据类型转成string类型

// 或者将string类型转成基本数据类型 package main

import (
	"fmt"
	_ "unsafe"
	"strconv"
)

func main()  {

	var num1 int = 99
	var num2 float64 = 22.222
	var b bool = true
	var mychar byte = 'h'
	var str string	//空的str

	// 使用第一种方式来转换 fmt.Sprintf方法

	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%c", mychar)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 第二种方式转换 strconv 函数

	var num3 int = 99
	var num4 float64 = 23.456
	var b2 bool = true

	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 'f'格式;10表示小数部分保留几位;64表示这个小数是float64
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)
	// 输出:str type string str="23.4560000000"

	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 第三种 strconv包中有一个函数Itoa,可以直接把一个int转为string
	var num5 int = 4567
	str = strconv.Itoa(num5)
	fmt.Printf("str type %T str=%q\n", str, str)
}
3.1.6.3 基本数据类型转换
package main
import (
	"fmt"
	_ "unsafe"  // 如果我们没有使用到一个包,但是不想去掉,就在前边加一个 _ 表示忽略
)

func main()  {
	var i int = 100

	// 将i ==> float

	var n1 float32 = float32(i)
	var n2 int8 = int8(i)
	var n3 int64 = int64(i)

	fmt.Printf("i=%v n1=%v n2=%v n3=%v\n", i, n1, n2, n3)

	// Go中数据类型的转换可以是从表示范围小-->表示范围大 也可以范围大-->范围小
	// 被转换的是变量存储的数据(即值),变量本身的数据类型并没有发生变化

	// 在转换中,比如讲int64 转换成 int8 [-128~127] ,编译时不会报错
	// 只是转换的结果是按照溢出处理的,和我们希望的结果不一样
	var num1 int64 = 999999
	var num2 int8 = int8(num1)
	fmt.Println("num2=", num2)
	// 输出结果:num2= 63

	var (
		m1 int32 = 12
		m2 int64
		m3 int8
	)
	m2 = int64(m1) + 20
	m3 = int8(m1) + 20

	fmt.Println("m2=", m2, "m3=", m3)

	var (
		b1 int32 = 12
		b2 int8
		// b3 int8
	)
	b2 = int8(b1) + 127		// 编译通过,但是结果不是127+12,按照溢出处理,输出结果为·117
	// b3 = int8(b1) + 128  编译不通过

	fmt.Println(b2)
}