【Golang学习编】

1.环境安装配置

Visual Studio Code下载地址:https://code.visualstudio.com/download

安装后切换中文:
1.ctrl+shift+p
2.再搜索Configure Display Language
3.显示中文插件点击Install

搭建Go开发环境,安装和配置SDK
SDK下载地址:https://golang.google.cn/dl
按系统选择
在这里插入图片描述
下载解压后看到的Go目录就是SDK
校验GO的SDK是否安装成功

配置Golang的环境变量:

环境变量
GOROOTD:\GO\go
GOPATHD:\GO\go\bin
上面是新建环境变量,下面是编辑环境变量
Path%GOROOT%\bin

GOPATH为项目存放的路径,即工作目录

校验环境变量是否配置好
重现打开cmd,不在D:\GO\go\bin路径下面输入go version。显示版本号即配置成功
在这里插入图片描述

Windows开发步骤:
1)安装windows版本VScode.
2)将Go代码编写到扩展名为hello.go的文件中
[说明:源码在工作目录src/go_code下,如:goproject/src/go_code]
3)通过go build命令对该go文件进行编译,生成.exe文件
在这里插入图片描述
4)在dos命令下执行.exe文件就可以看到运行效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/8e2b1eabddcb49a0b00013e2d2be66a6.png

5)注意:go run命令能直接运行hello.go程序
![在这里插入图片描述](https://img-blog.csdnimg.cn/f21edd4a6c3c4dd59482b97b37f0c45b.png

代码:
在这里插入图片描述

对上图的说明:

1.go文件的后缀为.go
2.package main 表示该hello.go文件所在的包是main,在go中,每个文件必须归属一个包
3.import “fmt” 表示引入一个包,包名fmt,引入该包后才能使用fmt包的函数,比如fmt.Println
4.func main func表示一个关键字,表示一个函数 main为函数名,是个主函数,为程序的入口
5.fmt.Println(“hello world”) 表示fmt包的函数Println输出"hello world"

Golang执行流程分析
1.go文件-------->(go build编译)编译成.exe可执行文件-------->(运行)结果
2.go文件-------->(go run编译运行)结果

两种执行的区别
第1种 编译成可执行文件,没有开发环境也可以运行。并且可执行文件内存大
第2种 依赖开发环境才可运行,文件内存小

编译和运行说明
1)有了go源文件,通过编译器将其编译成机器可以识别的二进制文件
2)在该源文件的目录底下,可通过go build对hello.go文件进行编译。可指定生成可执行文件名,在windows下必须是.exe后缀
在这里插入图片描述
在这里插入图片描述
3)如果程序没有错误,是没有任何提示,会在当前目录底下生成一个可执行文件,该文件是二进制,也是可执行的程序
4)如果程序有错误,编译时会在错误那行报错。精准到第X行报错
在这里插入图片描述
在这里插入图片描述

2.Go程序开发注意事项

1.Go源文件以go为后缀名
2.Go应用程序的执行入口是main()函数
3.Go语言严格区分大小写
4.Go方法由一条条语句构成,每个语句后不需要分号(Go语言会在每个语句后自动加分号),这也体现Golang的简洁性
5.Go编译器是一行行编译的,因此我们一行写一条语句,不能将多个语句写在同一行。否则报错
6.Go语言定义的变量或者import的包没有使用,代码不能编译通过
7.大括号是成对出现的,缺一不可

在这里插入图片描述
在这里插入图片描述

3.Golang常用的转义字符

1)\t,一个制表位,实现对齐功能

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("大家\t晚上好")
}

输出:大家 晚上好
2)\n,换行符

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("大家\n晚上好")
}

输出:
大家
晚上好

3)\\,一个\

当你想输出一个文件路径,需要这样输出
package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("D:\\goproject\\src\\go_code\\chapter02\\escaptechar")
}

输出:D:\goproject\src\go_code\chapter02\escaptechar

4)\",一个"

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("她说:\"她很开心\"")
}

输出:她说:“她很开心”

5)\r,一个回车,fmt.Println("大家\r晚上好")

//从当前行的最前面开始输出,覆盖掉以前内容
package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("大家哈哈哈哈哈\r晚上好")
}

输出:晚上好哈哈哈哈


课堂练习,请使用一句输出语句,达到输出如下图形的效果
在这里插入图片描述
源码

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//演示转义字符的使用
	fmt.Println("姓名\t年龄\t籍贯\t住址\nJohn\t12\t河北\t北京")
}

Golang开发常见问题和解决方法
找不到文件

D:\goproject\src\go_code\chapter02\escaptechar>go build apple.go
no required module provides package apple.go: go.mod file not found in current directory or any parent directory; see 'go help modules'

D:\goproject\src\go_code\chapter02\escaptechar>go run apple.go
CreateFile apple.go: The system cannot find the file specified.

D:\goproject\src\go_code\chapter02\escaptechar>apple.exe
'apple.exe' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

解决方法
源文件不存在或者写错,当前路径错误


注释的两种方式:

1.//行注释
2./* */块注释

源代码示范注释使用

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
	//行注释
	fmt.Println("不想学习")
	/*块注释
	fmt.Println("想碎觉")
	*/
}

输出:不想学习

注意:块注释不允许有块注释嵌套

规范的代码风格

1)使用一次tab操作,实现整体向右边移动,用shift+tab整体往左移 2)或者使用gofmt来进行格式化

格式化前的代码

package main
import "fmt"//fmt包中提供格式化、输出和输入的函数
func main(){
		//不想学习
		fmt.Println("不想学习")
		//不想碎觉
		fmt.Println("不想碎觉")
			//不想打工
			fmt.Println("不想打工")
}

格式化后的输出内容:

D:\goproject\src\go_code\chapter02\escaptechar>gofmt main.go
package main

import "fmt" //fmt包中提供格式化、输出和输入的函数
func main() {
        //不想学习
        fmt.Println("不想学习")
        //不想碎觉
        fmt.Println("不想碎觉")
        //不想打工
        fmt.Println("不想打工")
}

格式化更改源文件代码,重现打开源文件代码是格式化后的效果

D:\goproject\src\go_code\chapter02\escaptechar>gofmt -w main.go

3)运算符两边习惯性加一个空格

package main
import "fmt" //fmt包中提供格式化、输出和输入的函数
func main() {
	//不想学习
	fmt.Println("不想学习")
	var num = 2 + 4 * 2
	fmt.Println(num)
}

4)Go语言代码风格

错误的编写格式

func main()
{
	fmt.Println("不想学习")
}

正确的编写格式

func main(){
	fmt.Println("不想学习")
}

Go语言设计思想:一个问题只有一个解决方法

5)行长约定,一行不超过80个字符,超过的使用换行展示,尽量体现代码格式化优雅

源代码示范

package main
import "fmt" //fmt包中提供格式化、输出和输入的函数
func main() {
	//不想学习
	fmt.Println("这是很长的一段话有多长我们数一数\n",
	"一二三四五六七八九十一二三四五六七八九十\n",
	"一二三四五六七八九十一二三四五六七八九十\n",
	"一二三四五六七八九十一二三四五六七八九十\n",
	"一二三四五六七八九十\n",)
}

输出内容:

D:\goproject\src\go_code\chapter02\escaptechar>go run main.go
这是很长的一段话有多长我们数一数
 一二三四五六七八九十一二三四五六七八九十
 一二三四五六七八九十一二三四五六七八九十
 一二三四五六七八九十一二三四五六七八九十
 一二三四五六七八九十

4.Golang官方编程指南

Golang官方网站 https://golang.org/#
具体使用说明查看 bilibili网站【尚硅谷】Golang入门到实战教程丨一套精通GO语言,P24课节

Golang标准库API文档
1)API是Golang提供的基本编程接口
2)Go语言提供了大量的标准库,因此谷歌公司也为这些标准库提供了相应的API文档,用于告诉开发者如何使用这些标准库,以及标准库包含的方法。
3)Golang中文网,在线标准文档:https://studygolang.com/pkgdoc

5.Dos(Disk Operating System磁盘操作系统)的常用命令

1.dir查看当前所在路径底下的目录文件
2.C盘切换到D盘,输入d:
在这里插入图片描述
3.绝对路径:从当前盘的最上面开始定位,比如C:D找到对应的文件目录,比如D:\testb
相对路径:从当前位置开始定位,去找对应的目录。比如testb
4.返回上一步,cd ..
5.回到根目录,cd \

6.目录操作

1.新建目录,md testfile
2.新建多个目录,md testfile1 testfile2
3.删除空目录,rd testfile
4.删除目录底下的子目录和文件不带询问,rd /q/s testfile
5.删除目录底下的子目录和文件带询问,rd /s testfile

7.文件操作

1.输出hello内容到新建的test.txt文件(绝对路径),echo hello >d:\testa\test.txt
输出hello内容到新建的test.txt文件(相对路径),echo hello >test.txt
2.复制test.txt文件到指定路径,copy test.txt D:\testb\testbb
复制test.txt文件到指定路径并生成指定的文件名,copy test.txt D:\testb\testbb\test2.txt
3.移动test.txt文件到指定路径,move test.txt D:\testb\testbb
4.删除test.txt文件,del test.txt 删除所有.txt后缀的文件,del *.txt

8.其他指令
清屏,cls
退出,exit


6.变量(程序的基本组成单位)

变量使用的基本步骤:1.声明变量 2.变量赋值 3.使用变量

package main
import "fmt"
func main(){
	var pro int //声明变量
	pro = 10	//变量赋值
	fmt.Println(pro)	//使用变量

}

输出:10


变量使用的注意事项
1.变量表示内存中的一个存储区域
2.该区域有自己的名称(变量名)和类型(数据类型)
3.Golang使用的三种方式

1)指定变量类型,声明后若不赋值,使用默认值

package main
import "fmt"
func main(){
	var pro int //声明变量
	fmt.Println(pro)	//使用变量

}

输出:0

2)根据值自行判定变量类型(类型推倒)

package main
import "fmt"
func main(){
	var pro  = 10	//变量赋值
	fmt.Println(pro)	//使用变量

}

输出:10

3)省略var,注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误

package main
import "fmt"
func main(){
	name :="zhangsan"
	fmt.Println(name)	//使用变量

}

输出:zhangsan

4.多变量声明
在编程中,有时我们需要一次性声明多个变量,Golang提供这种语法

package main
import "fmt"
func main(){
	//方法1:声明,赋值,使用
	var a,b,c int
	a=0
	b=1
	c=2
	//方法2:声明赋值,使用
	var e,f,g int=3,4,5
	//方法3:类型推倒,使用
	h,i,j := 6,"不想上班",7
	fmt.Println(a,b,c,e,f,g,h,i,j)	//使用变量
}

输出:0 1 2 3 4 5 6 不想上班 7

全局变量声明赋值

package main
import "fmt"
//全局变量方法1:
var a = 100
var b = 200
var c = 300

//全局变量方法2:
var (
	d=400
	e=500
	f=600
)

func main(){
	fmt.Println(a,b,c,d,e,f)	//使用变量
}

输出:100 200 300 400 500 600
5.该区域的数据值可以在同一类型范围内不断变化

package main
import "fmt"
func main(){
	//该区域的数据值可以在同一类型范围内不断变化
	var i int
	i=1
	i=2
	fmt.Println(i)	//使用变量
	i=3.11//执行会报错,强制类型转换
}

输出:2
6.变量在同一作用域(在一个函数或者代码块)不能重名

package main
import "fmt"
func main(){
	var i int
	i=1
	fmt.Println(i)	
	//下面两种情况,变量在同一作用域重名,会报错的
	// var i int=2
	// i :=3
}

7.变量=变量名+值+数据类型。变量三要素
8.Golang的变量如果没有赋予初始值,编译器会使用默认值。比如int类型的默认值为0,string的默认值为空串。小数默认为0

程序中的+号使用
1.当左右两边都是数值,会做加法运算
2.当左右两边都是字符串,则做字符串拼接。

7.数据类型

1.基本数据类型:数值型(整点类型,浮点类型)、字符型、布尔型、字符串型
2.浮生/复杂数据类型:指针、数组、结构体、管道、函数、切片、接口、map

整型的类型
有符号:int8、int16、int32、int64
无符号:uint8、uint16、uint32、uint64
rune(与int32一样),byte(与uint8一样)

整型的使用细节:
1.Golang各整数类型的区分:有符号和无符号,int和uint大小和系统有关
2.Golang的整数默认声明是int
3.如何在程序查看变量的字节大小和数据类型
输出变量的字节大小%d
输出变量的数据类型%T

package main
import (
	"fmt"
	"unsafe"
)
func main(){
	var test int64 = 10
	fmt.Printf("test的类型是:%T,test的字节数是:%d",test,unsafe.Sizeof(test))

}

输出:test的类型是:int64,test的字节数是:8

4.Golang程序中整型变量在使用时,遵守保小不保大的原则,即:在保证程序正确运行下,尽量使用占用空间小的数据类型
5.bit是计算机最小的存储单位,byte是计算机最小的存储单元。1byte字节=8bit位

小数类型/浮点型

使用演示:

package main
import (
	"fmt"
)
func main(){
	var test float32 = 10.3
	fmt.Println(test)
}

输出:10.3

浮点型分类,单精度float32(4字节),双精度float64(8字节)
浮点数=符号位+指数位+尾数位
尾数部分可能丢失,精度损失。
float64精度要比float32的更准确。

浮点型使用细节:
1.Golang的浮点类型有固定的范围和字节长度,不受具体OS(操作系统)的影响
2.Golang的浮点型默认声明为float4类型
3.浮点常量有两种表示形式
十进制形式:5.12 .512(必须有小数点)
科学计数法形式:5.1234e2=5.12*10的2次方 5.12E*2=5.12/10的2次方

字符类型(char)

Golang中没有专门的字符了下,如果要存储单个字符(字母),一般有byte来报错。
字符串就是一串固定长度的字符连接起来的字符序列,Go的字符串是由单个字节连接起来的。也就是说对于传统的字符串是有字符组成的。而Go的字符串不太,它是由字节组成的。

[官方将string归属到基本数据类型:https://tour.go-zh.org/basics/11]

1.当我们直接输出byte值是输出对应的字符ASCII码值

package main
import (
	"fmt"
)
func main(){
	var test byte = 'a'
	fmt.Println(test)
}

输出:97

2.若我们需要输出对应的字符,需要使用格式化输出

package main
import (
	"fmt"
)
func main(){
	var test byte = 'a'
	fmt.Printf("%c",test)
}

输出:a
3.若我们需要输出对应的文字,使用int

package main
import (
	"fmt"
)
func main(){
	var test int = '北'
	fmt.Printf("test的码值为%d;\ntest的值为%c;",test,test)
}

输出:
test的码值为21271;
test的值为北

结论:
1.如果我们保存的字符在ASCII表的,比如[0-9,a-z,A-Z]可以保存到byte
2.如果我们保存的字符对应值大于255,这时我们可以考虑使用int类型
3.如果我们需要按照字符的方式输出,需要格式化输出fmt.printf(%c,test)


字符类型使用细节
1.字符常量是用单括号括起来的单个字符,例如 var test1 byte=‘a’、var test2 int=‘中’、var test3 byte=9
2. Go中允许使用转义字符’‘来将其后的字符转变成特殊字符型常量,例如 var test char=’\n’
3. Go语言的字符使用UTF-8编码,英文等于1个字节,汉字等于3个字节
4. 在Go中字符的本质是个整数,直接输出,是该字符对应的UTF-8编码的码值
5. 可以直接给某个变量赋一个数字,然后按格式化输出%c,会输出该数字对应的Unicode字符
符类型是可以运算的,相当于一个整数,它都有对应的Unicode码

package main
import (
	"fmt"
)
func main(){
	var test int = 20013
	fmt.Printf("test的码值为%d;\ntest的值为%c;",test,test)
}

输出:
test的码值为20013;
test的值为中;

  1. 字符类型是可以运算的,相当于一个整数,它都有对应的Unicode码
 package main
import (
	"fmt"
)
func main(){
	var test = 10+'a'
	fmt.Println(test)
}

输出:107

字符类型本质探讨
存储:字符>>字符对应的码值>二进制>>存储
读取:二进制>>字符对应的码值>>字符>>读取

布尔类型
1.布尔类型也叫bool类型,只允许取值true或者false
2.bool类型占一个字节
3.boolean类型适用于逻辑运算,一般用于程序流程控制

使用方法

package main
import (
	"fmt"
)
func main(){
	var test = true
	fmt.Println(test)
}

输出:true


注意事项:
1.bool类型只能去true或者false

package main
import (
	"fmt"
	"unsafe"
)
func main(){
	var test = true
	fmt.Println(unsafe.Sizeof(test))
	test=1   //强制类型转换
}

会报错:.\main.go:9:7: cannot use 1 (untyped int constant) as bool value in assignment

2.不可0或1代替true和false


字符串类型(string)
字符串就是一串固定长度的字符连接起来的字符序列,Go的字符串是由单个字节连接起来的。Go语言的字符字节是由UTF-8编码标识unicode文本

String基本使用

package main
import (
	"fmt"
)
func main(){
	var test string="北京长城  10086   abcd"
	fmt.Println(test)
}

输出:北京长城 10086 abcd

注意事项和使用细节
1.Go语言的字符串字节使用UTF-编码标识Unicode文本,这样Golang统一使用UTF-8编码。不会出现乱码问题
2.字符串一旦赋值了就不能改
3.字符串的两种表现形式
双引号:会识别转义字符
反引号:以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击,输出源代码等效果
反引号使用演示:

package main
import (
	"fmt"
)
func main(){
	var test string=`
	package main
	import (
		"fmt"
	)
	func main(){
		var test string="北京长城  10086   abcd"
		fmt.Println(test)
	}`//``为反引号
	fmt.Println(test)
}

输出:

        package main
        import (
                "fmt"
        )
        func main(){
                var test string="北京长城  10086   abcd"
                fmt.Println(test)
        }

4.字符串的拼接方式

package main
import (
	"fmt"
)
func main(){
	var test ="hello"+"xiaoming"
	fmt.Println(test)
}

输出:helloxiaoming
5.拼接内容太多,需要分行。并且+号留在上面一行最后面

package main
import (
	"fmt"
)
func main(){
	var test ="hello"+"xiaoming"+"hello"+
	"xiaoming"+"helloaa"+"xiaoming"+"hellobb"+
	"xiaoming"+"hellocc"+"xiaoming"
	fmt.Println(test)
}

输出:helloxiaominghelloxiaominghelloaaxiaominghellobbxiaominghelloccxiaoming

数据类型默认值

数据类型默认值
整型0
浮点型0
字符串“”
布尔类型false

代码示例格式化输出

package main
import (
	"fmt"
)
func main(){
	var a int 
	var b float32
	var c float64
	var d bool
	var e string
	fmt.Printf("a=%d,b=%f,c=%f,d=%v,e=%v",a,b,c,d,e)
	//%v为变量的值输出
}

输出:a=0,b=0.000000,c=0.000000,d=false,e=

8.Golang基本数据类型的转换

Golang和java/c不同,Go在不同类型的变量之间赋值就可以显式转换,也就是说Golang中数据类型不能自动转换

基本语法:
表达式:T(v)将值v转换成类型T
T就是数据类型
v就是要转换的变量

用法示例:

package main
import (
	"fmt"
)
func main(){
	var a int = 100
	var b float32 =float32(a)
	var c int8=int8(a)
	var d int64=int64(a)
	fmt.Printf("a的类型为%T,a的值为%v,\nb的类型为%T,b的值为%v,\nc的类型为%T,C的值为%v,\nD的类型为%T,D的值为%v",a,a,b,b,c,c,d,d)

}

输出:
a的类型为int,a的值为100,
b的类型为float32,b的值为100,
c的类型为int8,C的值为100,
D的类型为int64,D的值为100

细节说明:
1)Go中数据类型的转换是可以从表示范围小–>变成表示范围大,也可以表示范围大–>表示范围小
2)被转换的是变量存储的数据类型(即值),变量本身的数据类型没有变化
3)在转换中,比如将int64转换成int8,编译时不会报错,只是转换的结果按溢出处理,和我们希望的结果不一样

基本数据类型和String类型的转换

在程序开发中,我们经常需要将基本数据类型转换成string类型。或者将string类型转换成基本数据类型

基本类型转String类型
方式1:fmt.Sprintf(“%参数”,表达式)
1)参数需要和表达式的数据类型相匹配
2)fmt.Sprintf()会返回转换后的字符串
3)案例演示

package main
import (
	"fmt"
)
func main(){
	var a int = 100
	var b float64 =8.88
	var c bool = true
	var d byte ='k'
	var e string
	e=fmt.Sprintf("%d",a)
	fmt.Printf("d的类型为%T,d的值为%v,d的值为%q\n",e,e,e)
	e=fmt.Sprintf("%f",b)
	fmt.Printf("d的类型为%T,d的值为%v,d的值为%q\n",e,e,e)
	e=fmt.Sprintf("%t",c)
	fmt.Printf("d的类型为%T,d的值为%v,d的值为%q\n",e,e,e)
	e=fmt.Sprintf("%c",d)
	fmt.Printf("d的类型为%T,d的值为%v,d的值为%q\n",e,e,e)
}


输出:
d的类型为string,d的值为100,d的值为"100"
d的类型为string,d的值为8.880000,d的值为"8.880000"
d的类型为string,d的值为true,d的值为"true"
d的类型为string,d的值为k,d的值为"k"

方式二:使用strconv包的函数
案例演示

package main
import (
	"fmt"
	"strconv"
)
func main(){
	var a int = 99
	var b float64=9.9
	var c bool=true
	var str string
	//int类型转字符串
	str=strconv.FormatInt(int64(a),10)
	fmt.Printf("str的类型为%T,值为%q\n",str,str)
	str=strconv.Itoa(a)
	fmt.Printf("str的类型为%T,值为%q\n",str,str)//a只接受int类型,若a为int64需要转成int

	//float类型转字符串
	str=strconv.FormatFloat(b,'f',10,64)
	//说明strconv.FormatFloat(变量名,格式,小数位保留X位,小数的精度是X位)
	fmt.Printf("str的类型为%T,值为%q\n",str,str)
	
	//bool类型转字符串
	str=strconv.FormatBool(c)
	fmt.Printf("str的类型为%T,值为%q\n",str,str)
}

输出:
str的类型为string,值为"99"
str的类型为string,值为"99"
str的类型为string,值为"9.9000000000"
str的类型为string,值为"true"

String类型转换基本数据类型
1)使用strconv包
案例演示

package main
import (
	"fmt"
	"strconv"
)
func main(){
	//字符串类型转bool
	var str string="true"
	var a bool
	//a,_说明:strconv.ParseBool函数会返回两个值(value bool,error)
	//因为我只想获取value bool不想获取error,所以我使用_忽略
	a,_=strconv.ParseBool(str)
	fmt.Printf("a的类型为%T,值为%v\n",a,a)

	//字符串类型转int
	var str2 string="1234567"
	var b int64
	var c int 
	b,_=strconv.ParseInt(str2,10,64)
	c=int(b)
	//strconv.ParseInt(变量名,X进制,指定的整数类型),0(int)、8(int8)...
	fmt.Printf("b的类型为%T,值为%v\n",b,b)
	fmt.Printf("c的类型为%T,值为%v\n",c,c)

	//字符串类型转float
	var str3 string="16.66"
	var d float64
	d,_=strconv.ParseFloat(str3,64)
	fmt.Printf("d的类型为%T,值为%v",d,d)
}

输出:
a的类型为bool,值为true
b的类型为int64,值为1234567
c的类型为int,值为1234567
d的类型为float64,值为16.66

注意事项:
在将String转换成基本数据类型,需要确保String类型能够转换成有效数据,比如能将"123"转成一个整数,但不是将"hello"转换成整数,否则golang会转成默认值

9.指针

基本介绍
1)基本数据类型:变量存的是值,也叫值类型
2)获取变量的地址,用&,比如:var num int,取num的地址&num
3)指针类型,变量存的是一个地址,这个地址指向的空间存的才是值,比如var ptr *int=&ptr
4)获取指针类型所指向的值,使用 *,比如 var *ptr int,使用 *ptr获取ptr所指向的值

举例说明

package main
import (
	"fmt"
)
//演示golang的指针类型
func main(){
	//基本数据类型在内存布局
	var a int=10
	fmt.Println("a的地址=",&a)
	var b *int=&a
	//b是个指针变量,b的类型是*int,b本身的值是&a
	fmt.Printf("b的类型是%T,值是%v\n",b,b)
	fmt.Printf("b的地址是%v\n",&b)
	fmt.Printf("b所指向的值是%v\n",*b)
}

输出:
a的地址= 0xc00000c098
b的类型是*int,值是0xc00000c098
b的地址是0xc00000a030
b所指向的值是10


案例分析

声明一个int变量num,将num的地址赋值指向给指针ptr,通过ptr去修改num的值

package main
import (
	"fmt"
)
//演示golang的指针类型
func main(){
	//基本数据类型在内存布局
	var num int =10
	fmt.Println("num的地址是",&num)
	var ptr *int
	ptr=&num
	*ptr=11//这里修改,会修改到num变化
	fmt.Printf("num的值是%v",num)
}

输出:
num的地址是 0xc000102058
num的值是11

常见的值类型和引用类型
值类型:int系列、float系列、bool、char、string、数组和结构体struct
引用类型:指针、slice切片、map、管道chan、interface等都是引用类型

10.标识符的命名规则

标识符概念
1)Golang对各种变量、方法、函数等命名时所使用的字符序列称为标识符
2)凡是自己可以起名字的都称为标识符

标识符的命名规则
1)由26个字母大小写0-9,_组成
2)数字不可以开头
3)Golang中严格区分大小写
4)标识符不能包含空格
5)下划线本身在Go中是个特殊的字符,称为空标识符。可以代表任何其他的标识符、但是它对应的值会被忽略,所以它不能作为占位符使用,不能作为标识符使用
6)不能以系统保留关键字作为标识符,比如break,if等等

标识符的注意事项
1)包名:保持package的名字和目录保持一致,尽量使用有意义的包名,剪短,有意义,不要和标准库冲突
在这里插入图片描述

2)变量名、函数名、常量名尽量使用驼峰法
3)如果变量名、函数名、常量名首字母为大写则可以给其他包使用,如果首字母小写,则只能在本包使用。
案例示范,使用其他包

在这里插入图片描述
在这里插入图片描述
输出:宋江

注意事项:
因为新版的go不支持老版本的环境配置了,若出现is not in GOROOT。可以输入命令go env确认GO111MODULE为""或者on,需要输入命令修改go env -w GO111MODULE=off并重启cmd,执行go run main.go

11.运算符介绍

运算符是一种特殊符号,用于表示数据的运算、赋值和比较等
1)算术运算符
2)赋值运算符
3)比较运算符
4)逻辑运算符
5)位运算符
6)其他运算符

1)算术运算符
算术运算符是对数值类型的变量进行运算,加减乘除在Go程序使用非常多
细节说明:
1)对于"/"号,它的整数除和小数除是有区别的,整数除只保留整数部分,舍弃小数部分。
2)当一个数取模时,可以等价a%b=a-a/b*b
3)Golang的自增自减,只能当做一个独立语言使用,不能这样使用:
b:=a++或者b:=a–
4)Golang的++和–只能写在变量后面
5)Golang的设计者去掉java/c中的自增自减容易混淆的写法,让golang更加简洁统一

2)比较运算符
比较运算符结果都是bool型,也就是要么true,要么是false
关系运算符通常在if结构条件或者循环条件使用较多
在这里插入图片描述
细节说明:
1)比较运算符的结果都是bool类型
2)比较运算符的表达式,我们称为关系表达式:a>b
3)比较运算符"==“不能写成”="

3)逻辑运算符
用于连接多个条件(一般来讲是关系表达式),最终结果也是bool型
在这里插入图片描述
细节说明:
1)&& 也叫短路与,如果第一个条件为false,第二个条件就不会进行判断,最终结果为false
2)|| 也叫短路或,如果第一个条件为true,第二个条件就不会进行判断,最终结果为true

4)赋值运算符
赋值运算符是将某个运算后的值,赋给指定的变量
在这里插入图片描述
在这里插入图片描述
运算符优先级
1)运算符有不同的优先级,所谓优先级就是表达式运算中的运算顺序,如下表,上一行运算符总优先于下一行。
2)只有单目运算符,赋值运算符是从右往左运算的。
3)大致的顺序整理
1.括号,++,–
2.单目运算
3.算术运算
4.移位运算
5.比较运算符
6.位运算符
7.逻辑运算符
8.赋值运算符
9.逗号

12键盘输入语句

介绍
在编程中需要接收用户输入的数据,就可以使用键盘输入语句来获取。ImputDemo.go

步骤:
1)导入fmt包
2)调用fmt.Scanln()或者fmt.Scanf()

案例演示:
从控制台接收用户信息(姓名,年龄,薪水,性别)
1)使用fmt.Scanln()

package main
import "fmt"
func main() {
	var name string
	var age int
	var sal float32
	var isPass bool
	fmt.Println("请输入你的名字")
	fmt.Scanln(&name)
	fmt.Println("请输入你的年龄")
	fmt.Scanln(&age)
	fmt.Println("请输入你的薪水")
	fmt.Scanln(&sal)
	fmt.Println("请问你是学生吗")
	fmt.Scanln(&isPass)

	fmt.Printf("你的名字是%v,年龄是%v,薪水是%v,是否是学生 %v", name, age, sal, isPass)
}

输出:
请输入你的名字
西瓜
请输入你的年龄
18
请输入你的薪水
1234.56
请问你是学生吗
true
你的名字是西瓜,年龄是18,薪水是1234.56,是否是学生 true

2)使用fmt.Scanf()

import "fmt"
func main() {
	var name string
	var age int
	var sal float32
	var isPass bool
	fmt.Println("请输入你的名字,年龄,薪水,是否是学生  请用空格隔开")
	fmt.Scanf("%s %d %f %t", &name, &age, &sal, &isPass)
	fmt.Printf("你的名字是%v \n你的年龄是%v \n你的薪水是%v \n是否是学生%v", name, age, sal, isPass)
}

输出:
请输入你的名字,年龄,薪水,是否是学生 请用空格隔开
西瓜 18 99.99 true
你的名字是西瓜
你的年龄是18
你的薪水是99.99
是否是学生true

13.进制

进制介绍
对于整数,有四种表示方式
1)二进制:0,1,满2进1
2)十进制:0-9,满10进1
3)八进制:0-7,满8进1,以数字0开头显示
4)十六进制:0-9及A-F,满16进1,以0x或者0X开头表示。此处A-F不区分大小写
如0x21AF+1=0X21B0**

案例输出:

package main
import "fmt"
func main() {
	//二进制输出
	var a int = 5
	fmt.Printf("%b \n", a)

	//八进制转换十进制
	var b int = 011
	fmt.Println("b=", b)

	//十六进制转换十进制
	var c int = 0x11
	fmt.Println("c=", c)
}

输出:
101
b= 9
c= 17

进制的转换
进制转换的介绍

第一组:(其他进制转十进制)
1)二进制转十进制
规则:从最低位开始(最右边的数值)将每个位上的数取出来,乘以2的(位数-1)次方,然后求和。
案例:请将二进制1011转成十进制的数
1011=11+12+022+1222=1+2+0+8=11
2)八进制转十进制
规则:从最低位开始(最右边的数值)将每个位上的数取出来,乘以8的(位数-1)次方,然后求和。
案例:请将八进制0123转成十进制的数
0123=3
1+28+188+0888=3+16+64+0=83
3)十六进制转十进制
规则:从最低位开始(最右边的数值)将每个位上的数取出来,乘以16的(位数-1)次方,然后求和。
案例:请将十六进制0x34A转成十进制的数
034A=101+416+316*16=10+64+768=842

第二组:(十进制转其他进制)
1)十进制转二进制
规则:该除数不断除以二,直到商为0为止。然后将每步得到的余数倒过来,就是对应的二进制
案例:请将56转成二进制
结果为11100
在这里插入图片描述
2)十进制转八进制
规则:该除数不断除以八,直到商为0为止。然后将每步得到的余数倒过来,就是对应的二进制
案例:请将156转成八进制
结果为234
在这里插入图片描述
3)十进制转十六进制
规则:该除数不断除以八,直到商为0为止。然后将每步得到的余数倒过来,就是对应的二进制
案例:请将356转成十六进制
结果为164
![在这里插入图片描述](https://img-blog.csdnimg.cn/871bd6c13f41465b8e953f1bc486058f.png

第三组:(二进制转其他进制)
1)二进制转八进制
规则:将二进制转每三位一组(从低位开始组成),转成对应的八进制即可。
案例:请将二进制11010101转成八进制
结果为:0325
在这里插入图片描述

2)二进制转十六进制
规则:将二进制转每四位一组(从低位开始组成),转成对应的十六进制即可。
案例:请将二进制11010101转成八进制
结果为0xD5
在这里插入图片描述

第四组:(其他进制转二进制)
1)八进制转二进制
规则:将八进制每一位,转成对应的一个3位的二进制即可
案例:请将0237转成二进制
结果为:10011111
2)十六进制转二进制
规则:将十进制每一位,转成对应的一个3位的二进制即可
案例:0x237转成二进制
结果为:1000110111

14.位运算

原码、反码、补码
对于有符号而言:
1)二进制最高位是符号位:0表示正数,1表示负数
2)正数的原码、反码和补码都一样
3)负数的反码=它的原码符号位不变,其他位取反(0>1,1>0)
-1==>原码10000001、反码11111110
4)负数的补码=它的反码+1
-1的补码=11111111
5)0的反码,补码都是0
6)在计算机运行的时候,都是以补码方式来运算

位运算符和移位运算符

位运算符
分别是按位与、按位或、按位异或。它的运算规则是

按位与&:两位全为1,结果才为1,否则为0
案例:2&3
2的补码是00000010
3的补码是00000011
两位全为1,结果才为1,否则为0。所以2&3结果为2

按位或|:两位有一个为1,结果为1,否则为0
案例:2|3
2的补码是00000010
3的补码是00000011
两位有一个为1,结果为1,否则为0。所以2|3结果为3

按位异^:两位一个为0,一个为1,结果为1,否则为0
案例:2^3
2的补码是00000010
3的补码是00000011
两位一个为0,一个为1,结果为1,否则为0。所以2^3结果为1

移位运算符
右移和左移的运算规则>>、<<
右移运算符>>:低位溢位,符号位不变,并用符号位补溢出的高低
左移运算符>>:符号位不变,低位补0

15.程序流程控制介绍

在程序中,程序运行的流程控制决定程序是如何执行的。主要有三大流程控制语句
1)顺序控制
程序从上往下逐行地执行,中间没有任何判断和跳转
2)分支控制
程序有选择的执行,分支控制有三种

  • 单分支
// 基本语法
 if	条件表达式{
 		执行代码块
 }
 //说明:当条件表达式为true时,就会执行{}的代码
  • 双分支
// 基本语法
if	条件表达式{
		执行代码块1
}else{
		执行代码块2
}
//说明:当条件表达式成立,会执行代码块1,否则执行代码块2。
  • 多分支
// 基本语法
if	条件表达式1{
		执行代码块1
}else if 条件表达式2{
		执行代码块2
}
else{
		执行代码块N
}
//说明:先判断条件表达式1,如果条件表达式1为true,则执行代码块1。否则进行判断条件表达式2,如果条件表达式2为true,则执行代码块2。否则执行代码块N.
//注意else不是必须
  • 嵌套分支
 if	条件表达式{
 	if	条件表达式{
 	}	else{
 	}
 }
 //在一个分支结构中又完整地嵌套了另一个完整地分支结构
 //里面的分支结构称为内层分支,外面的结构称为外层分支
 //嵌套分支不宜过多,控制在三层内。
  • switch分支结构
    1)switch语句用于基于不同条件执行不同的动作,每个case分支都是唯一的。从上到下逐一测试,直到匹配停止。
    2)匹配项后面不用再加break

基本语法

switch	表达式{
case	表达式1,表达式2...:
	语句块1
case	表达式3,表达式4...:
	语句块2
//这里可以有很多case语句	
default:
	语句块
}

Switch的注意事项和使用细节
1)case/switch后是个表达式(即常量值、变量、一个返回的函数值都可以)
2)case后的各个表达式的数据类型要和switch的表达式数据类型一致
3)case后面可以有多个表达式,使用逗号分隔开。
4)case后面的表达式如果是常量值,则要求不重复。
5)case后面不需要带break
6)default语句不是必须的
7)switch后也可以不带表达式,类似if-else分支来使用
8)switch后声明定义一个变量,分号结束(不推荐使用)
9)switch穿透fallthrough,则会执行下一个case.也叫switch穿透
10)Type Switch:switch语句还可用于type switch来用于判断某个interface变量中的实际指向的变量类型

switch和if的比较
1)如果判断的具体数值不多,而且符合整数,浮点数,字符和字符串几种类型。建议使用switch语句简洁高效。
2)其他情况,对区间判断和结果bool类型的判断,if使用范围更广

3)循环控制

  • for循环基本语法:
for	变量初始化;循环条件;循环变量迭代{
	循环操作
}

案例:for循环,输出十句hello

//第一种写法
package main

import "fmt"

func main() {
	for a := 1; a <= 10; a ++ {
		fmt.Println("hello")
	}
}


//第二种写法
package main

import "fmt"

func main() {
	a := 1
	for a <= 10 {
		fmt.Println("哈哈哈")
		a++
	}
}

for循环使用注意事项
1.循环条件是个布尔值的表达式
2.for循环的使用方法

for{
	循环执行语句
}

//上面写法是个无限循环,需要使用break语句使用

3.golang提供for range的方式,可以方便遍历字符串和数组。

//传统写法
package main

import "fmt"

func main() {
	var str string = "abcdefg"
	for i := 0; i < len(str); i++ {
		fmt.Printf("%c \n", str[i])
	}
}

//for range写法

package main

import "fmt"

func main() {
	var str string = "abcdefg"
	for index, val := range str {
		fmt.Printf("index=%d,val=%c	\n", index, val)
	}
}

上面代码细节讨论,如果我们的字符串含有中文,那么传统的遍历字符串方式是错误的,会出现乱码。原因是传统的是按照字节遍历。一个汉字的utf-8编码占3个字节。解决方法:

package main

import "fmt"

func main() {
	var str string = "abcdefg哈哈"
	str2 := []rune(str)
	for i := 0; i < len(str2); i++ {
		fmt.Printf("%c \n", str2[i])
	}
}

练习题:
1)打印1-100之间,所有能被9整除的数值个数和总和。

package main

import "fmt"

func main() {
	var a int = 1
	var sum int = 0
	var count int = 0
	for ; a < 101; a++ {
		if a%9 == 0 {
			count++
			sum += a
		}
	}
	fmt.Printf("count=%d,sum=%d", count, sum)
}

2)完成下面的表达式输出

0 +6 =6
1 +5 =6
2 +4 =6
3 +3 =6
4 +2 =6
5 +1 =6
6 +0 =6

package main

import "fmt"

func main() {
	var b int = 6
	for a := 0; a <= 6; a++ {
		fmt.Printf("%d	+%d	 =%d\n", a, b-a, b)
	}

}

while和do…while的实现。
Go语言没有while和dowhile的写法,但可以使用for循环实现类似的效果
1)for循环实现while的效果
循环变量初始化

for{
	if	循环条件表达式{
		break//跳出for循环
	}
	循环操作语句
	循环变量迭代	
}

多重循环控制
1)将一个循环放在另一个循环体内,就形成了嵌套循环。在外边的for称为外层循环。在里面的for循环称为内层循环(建议不超过3层)
2)实质上,嵌套循环就是把内循环当作外循环的循环体。当只有内层循环的循环条件为false时,才能跳出内循环。才能结束外循环的当次循环,开始下一次循环。
3)设外循环的条件为m次,内循环的条件是n次,内层循环实际要执行m*n次

打印99乘法口诀

package main

import "fmt"

func main() {

	for i := 1; i <= 9; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%d * %d = %d  \t", j, i, j*i)
		}
		fmt.Println()
	}

}

跳转控制语句Break
随机生成1-100的数,直到生成99的数,退出循环

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var count int = 0
	for {
		rand.Seed(time.Now().UnixNano())
		n := rand.Intn(100) + 1
		fmt.Println("n=", n)
		count++
		if n == 99 {
			break
		}
	}
	fmt.Println("生成99一共使用了", count)
}

Break使用的注意事项,若break出现在多层嵌套语句中,可使用标签指定哪一层的语句块

使用示范:

package main

import "fmt"

func main() {
lable1:
	for a := 1; a <= 5; a++ {
		for b := 6; b <= 10; b++ {
			if a == 2 {
				break lable1
			}
			fmt.Println(b)
		}
		fmt.Println(a)
	}

}

输出:
6
7
8
9
10
1

练习题:
1.100以内的数值求和,求出当总和大于20的当前值

package main

import "fmt"

func main() {
	var sum int
	for a := 0; a <= 100; a++ {
		sum = a + sum
		if sum > 20 {
			fmt.Println("当sum大于20时,当前值为%d", a)
			break
		}

	}
}

2.实现登录验证,有三次机会。当用户名为张无忌,密码888提示登录成功,否则提示,你还有几次机会。

package main

import "fmt"

func main() {
	var name string
	var password int
	fmt.Println("请输入你的名字")
	fmt.Scanln(&name)
	if name == "张无忌" {
		for i := 3; i >= 0; i-- {
			fmt.Println("请输入你的密码")
			fmt.Scanln(&password)
			if password == 888 {
				fmt.Println("登录成功")
				break
			}
			if i == 1 {
				fmt.Println("3次机会用完,自动退出程序")
				break
			}
			fmt.Printf("你还剩%d次机会\n", i-1)
		}
	} else {
		fmt.Println("你不是张无忌")
	}

}

跳转控制语句continue
1、continue结束本次循环,进入下次循环
2、标签指定结束哪一层的循环
案例示范

package main

import "fmt"

func main() {
here:
	for i := 0; i < 2; i++ {
		for j := 1; j < 4; j++ {
			if j == 2 {
				continue here
			}
			fmt.Println("i=", i, "j=", j)
		}
	}

}

输出:
i= 0 j= 1
i= 1 j= 1

练习题:
1、用continue实现,1-100的奇数和。(for循环+continue)

package main

import "fmt"

func main() {
	var sum int = 0
	for i := 0; i <= 100; i++ {
		if i%2 == 0 {
			continue
		}
		sum = i + sum
	}
	fmt.Println("总数和为", sum)

}

2、从键盘输入个数不确定的正数或负数个数,输入为0结束程序。(for循环+break+continue)
package main

import "fmt"

func main() {
	var zhengshu int
	var fushu int
	var num int
	for {
		fmt.Println("请输入一个数值")
		fmt.Scanln(&num)
		if num == 0 {
			break //结束程序
		}
		if num > 0 {
			zhengshu++
			continue
		}
		fushu++
	}
	fmt.Printf("输入正数的个数为%d,负数的个数为%d", zhengshu, fushu)

}

跳转控制语句goto
1.go语言的goto语句,可以无条件转移到程序中的指定行
2.goto语句经常与条件语句配合使用,可以实现条件转移。跳出循环体功能
3.在go程序设计中一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序中产生困难

使用示范:

package main

import "fmt"

func main() {
	fmt.Println("111")
	goto label1
	fmt.Println("222")
label1:
	fmt.Println("333")
}

输出:
111
333

跳转控制语句return
1.return在普通的函数,代表跳出该函数,不再执行return后面的代码。也可以理解成终止函数
2.return在main函数,表示终止main函数,也称终止程序。

package main

import "fmt"

func main() {
	var a int = 10
	fmt.Println("111")
	if a > 5 {
		return
	}

	fmt.Println("222")
	fmt.Println("333")
}

输出:
111

函数
函数作用:利于维护、代码简洁
函数基本语法

func 函数名 (形参列表)(返回值类型列表){
	函数执行语句
	return	返回值类型列表
}

1.形参列表:函数的输入
2.函数中的语句:表示为了实现某一功能代码块
3.函数可以有返回值,也可以没有
案例示范:

package main

import "fmt"

func cal(n1 float64, n2 float64, operator byte) float64 {
	var res float64
	switch operator {
	case '+':
		res = n1 + n2
	case '-':
		res = n1 - n2
	case '*':
		res = n1 * n2
	case '/':
		res = n1 / n2
	}
	return res
}

func main() {
	var n1 float64 = 2.6
	var n2 float64 = 1.3
	var operator byte = '+'
	result := cal(n1, n2, operator)
	fmt.Println("result=", result)

}

16.包的介绍

包作用:
1.区分相同名字的函数、变量等标识符
2当程序文件很多时,可以很好的管理项目
3.控制函数、变量等访问范围,即作用域

打包基本语句package until
引入包的基本语法import “包的路径”

引入包注意事项
1.被引入包的函数名首字母,需要大写。
2.引入包的路径默认从Src开始写:例如go_code/project/utils
若被引入的文件路径为GoProject/Src/go_code/project/utils/abc.go
3.调用包的语法:abc.函数名()
若被引入的文件路径为GoProject/Src/go_code/project/utils/abc.go
4.在给一个文件打包时,该包对应一个文件夹。文件的包名和文件所在的文件夹名一致。一般为小写字母
被引入的文件路径为GoProject/Src/go_code/project/utils/utils.go
5.package指令在文件第一行,再是import
6.相同的包下面不能使用相同的函数名
7.只有main包才能打包成一个可执行文件

17.函数递归调用

函数递归遵守的重要原则:
1.执行一个函数时,就创建一个新的受保护空间(新函数栈)
2.函数的局部变量是独立的,不会相互影响
3.递归必须退出递归的条件逼近,否则是无限递归
4.当一个函数执行完毕,或者遇到return就会返回。遵守谁调用,把结果返回谁
代码示例:

package main

import "fmt"

func test(n int) {
	if n > 2 {
		n--
		test(n)
	}
	fmt.Println("n=", n)
}

func main() {
	test(4)

}

输出:
n= 2
n= 2
n= 3

递归练习题真难,做不出来,整理笔记先这样。后面可以重温
https://www.bilibili.com/video/BV1ME411Y71o/?p=117

函数的注意事项和细节讨论
1.函数的形参列表可以是多个,返回值也可以是多个。
2.形参和返回值的列表数据类型可以是值类型也可以是引用类型
3.函数的命名遵循标识符的命名规范,首字母大写可以被本包和其他包文件调用
4.函数的变量是局部的,函数外不生效
5.基本数据类型和数组默认是值传递的,即进行值拷贝,在函数内修改,不会影响到原来的值
6.如果希望函数内的变量能修改函数外的变量,可以引入变量的地址&,函数内以指针的方式操作变量
7.Go函数不支持重载
8.在Go中,函数也是一种数据类型,也可以赋值给一个变量,则该变量就是函数类型的变量了。通过该变量可以对函数调用
9.函数既然是一种数据类型,可以作为形参,并且调用
10.为了简化数据类型定义,Go支持自定义数据类型
11.支持对函数返回值命名
12.使用_标识符,忽略返回值
13.Go支持可变参数

Init函数
每个源文件都包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。init函数最主要的作用是初始化工作
代码示例:

package main

import "fmt"

func init() {
	fmt.Println("init函数输出")

}

func main() {
	fmt.Println("main函数输出")

}

输出:
init函数输出
main函数输出

init注意事项:如果一个文件中包含全局变量定义,Init函数和main函数。执行顺序为:全局变量>init函数>main函数

匿名函数
Go支持匿名函数,如果我们某个函数希望只使用一次,可以考虑匿名函数,匿名函数也可以实现多次调用。

匿名函数使用方式1:在定义匿名函数时直接调用

package main

import "fmt"

func main() {
	resl := func(n1 int, n2 int) int {
		return n1 + n2

	}(10, 20)
	fmt.Println("resl=", resl)

}

输出:resl= 30

匿名函数使用方式2:将匿名函数给一个变量(函数变量),再通过变量调用匿名函数

package main

import "fmt"

func main() {
	a := func(n1 int, n2 int) int {
		return n1 - n2

	}
	resl2 := a(10, 20)
	fmt.Println("resl2=", resl2)
}

输出:resl2= -10

匿名函数使用方式3:将匿名函数给一个全局变量,那么这个函数为全局匿名函数

package main

import "fmt"

var (
	a = func(n1 int, n2 int) int {
		return n1 * n2
	}
)

func main() {
	res3 := a(2, 3)
	fmt.Println("res3=", res3)
}

输出:res3= 6

闭包
基本介绍:闭包是指一个函数和与其相关环境组合的一个整体(实体)

代码示例:

package main

import "fmt"

func AddUpper() func(int) int {
	var n int = 10
	return func(x int) int {
		n = n + x
		return n
	}
}

func main() {
	f := AddUpper()
	fmt.Println(f(1)) //结果为11
	fmt.Println(f(2)) //结果为13
	fmt.Println(f(3)) //结果为16

}

对上面代码说明和总结
1.AddUpper是个函数,返回的数据类型是func(int) int
2.闭包说明

var n int = 10
return func(x int) int {
	n = n + x
	return n

返回的是个匿名函数,但这个匿名函数引用到函数外的n。因此这个函数和n就形成一个整体,构成闭包。
3.大家可以理解,闭包是类,函数是操作,n是字段。函数和它使用n构成闭包
4.当我们反复的调用f函数时,因为n是初始化一次,因为它每调用一次就会进行累加
5.我们要搞清楚闭包的关键,就是要分析出返回的函数使用到哪些变量。因为函数和它引用的变量会共同构成闭包

闭包的最佳实践
请编写一个程序,具体要求如下。
1.编写一个函数makesuffix(suffix string)可以接收一个文件名后缀(比如.jpg),并返回一个闭包
2.调用闭包,可以传入一个文件名。如果该文件名没有指定后缀名(比如.jpg),则返回文件名.jpg。如果已经有jpg后缀,则返回原文件名。
3.要求使用闭包的方式完成
4.strings.HasSuffix.该函数能判断某个字符串有指定的后缀

package main

import (
	"fmt"
	"strings"
)

func makesuffix(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}

func main() {
	f := makesuffix(".jpg")
	fmt.Println("文件名处理后=", f("winter"))
	fmt.Println("文件名处理后=", f("bird.jpg"))

}

输出:
文件名处理后= winter.jpg
文件名处理后= bird.jpg

代码说明:
1.返回的函数和makesuffix(suffix string)的stuffix变量和返回的函数组合成一个闭包。因为返回的函数引入到stuffix这个变量。
2.我们体会下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能。只是传统方法需要每次都传入后缀名,比如.jpg。而闭包因为可以保留上次引入的某个值。所以我们使用一次就可以反复使用。

函数中derfer
在函数中,程序员经常需要创建资源(比如:数据库连接,文件句柄,锁等),为了在函数执行完毕后,及时释放资源。提供defer可以延时机制。

代码示例:

package main

import (
	"fmt"
)

func sum(n1 int, n2 int) int {
	//当执行到defer暂时先不执行,会将defer后面的语句压入到独立的栈(defer栈)
	//当函数执行完毕后,再从栈中以先进后出方式出栈。执行顺序分别是
	defer fmt.Println("ok1 n1=", n1) //3.ok1 n1=10
	defer fmt.Println("ok2 n2=", n2) //2.ok2 n2=20

	res := n1 + n2
	fmt.Println("ok3 res=", res) //1.ok3 res=30
	return res
}

func main() {
	res := sum(10, 20)
	fmt.Println("res=", res) //4.res= 30

}

输出:
ok3 res= 30
ok2 n2= 20
ok1 n1= 10
res= 30

defer的细节说明:
1.当go执行到第一个defer时,不会立即执行defer后的语句。而是将defer语句压到一个栈。然后继续执行到下个语句。
2.当执行完毕后,再从栈中,依次从栈顶取出语句执行。遵循先入后出的机制
3.在defer将语句放入栈时,会将相关值同时copy入栈

package main

import (
	"fmt"
)

func sum(n1 int) int {
	defer fmt.Println("n1=", n1)
	n1++
	return n1
}

func main() {
	n2 := sum(10)
	fmt.Println("n2=", n2)

}

输出:
n1= 10
n2= 11

defer最主要价值是在于:当函数执行完毕后,可以及时释放函数创建的资源。在defer执行完后,能继续使用创建资源。当函数执行完毕后,系统会依次从defer栈中取出语句,关闭资源。

func test() {
	file = openfile(文件名)
	def.file.close()//关闭文件资源
}

func test() {
	connect = openDatabase(文件名)
	def.connect .close()//关闭数据库资源
}

函数参数的传递方式
1.值传递2.引用传递
值传递的是指copy,引用传递的是指地址的copy.地址copy效率高

值类型和引用类型
1.值类型的基本类型是int、float、string、bool、数组和结构体struct
2.引用类型是:指针,slice切片、map、管道chan、interface等引用类型

变量作用域
1.函数内部声明/定义的变量叫局部变量,作用域仅限于函数内
2.函数外部声明/定义的变量叫全局变量,作用域是整个包有效。如果首字母为大写,作用域在整个程序有效。
3.如果变量在一个代码块,比如for/if,那么作用域就仅限于代码块

18.字符串常用的系统函数

1.统计字符串的长度,按字节len(str)
2.字符串遍历,同时处理有中文的问题。r:=[]rune(str)
代码示例:

package main

import (
	"fmt"
)

func main() {
	a := "大家好hello"
	b := []rune(a)
	for i := 0; i < len(b); i++ {
		fmt.Printf("字符=%c\n", b[i])
	}
}

输出:
字符=大
字符=家
字符=好
字符=h
字符=e
字符=l
字符=l
字符=o

3.字符串转整数:n,err:=strconv.Atoi(“12”)
代码示例:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	a, b := strconv.Atoi("12")
	if b != nil {
		fmt.Println("转换失败_01", b)
	} else {
		fmt.Println("转换成功_01", a)
	}

	c, d := strconv.Atoi("hello")
	if d != nil {
		fmt.Println("转换失败_02", d)
	} else {
		fmt.Println("转换成功_02", c)
	}
}

输出:
转换成功_01 12
转换失败_02 strconv.Atoi: parsing “hello”: invalid syntax

4.整数转字符串:
str=strconv.ltoa(12345)

	str := strconv.Itoa(12345)
	fmt.Printf("str=%v\n str=%T", str, str)

5.字符串转byte: var bytes=[]byte(“hello”)

var bytes = []byte("hello")
fmt.Printf("bytes=%v\n", bytes)

6.byte转字符串:str:=string([]byte{97,98,99})

str := string([]byte{97, 98, 99})
fmt.Printf("str=%v\n", str)

7.10进制转2、8、16进制:str:=strconv.FormatInt(123,2)

	str := strconv.FormatInt(123, 2)
	fmt.Printf("123对应的二进制为%v\n", str)
	str1 := strconv.FormatInt(123, 16)
	fmt.Printf("123对应的十六进制为%v\n", str1)

8.查找子串是否在指定的字符串中:strings.Contains(“saeafood”,“foo”)//若存在,返回布尔值

a := strings.Contains("saeafood", "foo")
fmt.Printf("a=%v", a)

9.统计一个字符串有几个指定的子串:strings.Count(“applese”,“p”)//输出为2

a := strings.Count("applese", "p")
fmt.Printf("a=%v", a)

10.不区分大小写的字符串比较(==是区分大小写):fmt.Println(strings.EqualFold(“abc”,“ABC”))
11.子串在字符串中第一次出现index的值,如果没有返回-1,strings.Index(“NLT_abc”,“abc”)

index := strings.Index("NLT_abc", "abc")
fmt.Printf("index=%v\n", index)
index1 := strings.Index("NLT_abc", "g")
fmt.Printf("index1=%v", index1)

输出:
index=4
index1=-1

12.返回子串最后一次在字符串出现的Index,如果没有返回-1,strings.LastIndex(“NLT_abc”,“abc”)
13.将指定的子串替换成另外一个字符串:strings.Replace(“go hello”,“go”,“go语言”,n),n可以指定你希望替换几个,如果n=-1全部替换

a := "no go go hello yes"
b := strings.Replace(a, "go", "go语言", -1)
fmt.Println("b=", b)

输出:b= no go语言 go语言 hello yes

14.按照指定的某个字符,为分割标识符,将一个字符串拆分成字符串数组:strings.Split(“hello,wrold,ok”,“,”)

a := strings.Split("hello,wrold,ok", ",")
fmt.Println("a=", a)

输出:a= [hello wrold ok]

15.将字符串字母进行大小写转换:strings.ToLower(“Go”)转小写,strings.ToUpper(“Go”)转大写
16.将字符串左右两边的空格去掉:strings.TrimSpace(" Go “)
17.将字符串左右两边指定字符去掉:strings.Trim(”!Go!“, “!”)
18.将字符串左边的指定字符去掉:strings.TrimLeft(”!Go!“, “!”)
19.将字符串右边的指定字符去掉:strings.TrimRight(”!Go!", “!”)
20.判断字符串是否以指定的字符串开头:strings.HasPrefix(“https://www:baidu.com”, “https:”)
21.判断字符串是否以指定的字符串结束:strings.HasSuffix(“https://www:baidu.com”, “.com”)

19.时间和日期相关的函数

时间和日期相关函数,需要导入time包
1.time.Time类型,表示时间()

package main

import (
	"fmt"
	"time"
)

func main() {
	a := time.Now()
	fmt.Printf("a=%v,类型为%T", a, a)
}

输出:a=2023-11-11 20:50:37.9225572 +0800 CST m=+0.019005301,类型为time.Time
2.获取当前时间方法:time.Now()
3.如何获取其他日期信息

now := time.Now()
fmt.Printf("now=%v,类型为%T\n", now, now)
fmt.Printf("年=%v\n", now.Year())
fmt.Printf("月=%v\n", now.Month())      //英文输出
fmt.Printf("月=%v\n", int(now.Month())) //数字输出
fmt.Printf("日=%v\n", now.Day())
fmt.Printf("时=%v\n", now.Hour())
fmt.Printf("分=%v\n", now.Minute())
fmt.Printf("秒=%v\n", now.Second())

输出:
年=2023
月=November
月=11
日=11
时=21
分=10
秒=18

4.格式化日期和时间
1)格式化第一种方法:

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Printf("当前年月日为:%02d-%02d-%02d  %02d:%02d:%02d\n",
		now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second(),
	)
	now2 := fmt.Sprintf("当前年月日为:%02d-%02d-%02d  %02d:%02d:%02d\n",
		now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second(),
	)
	fmt.Println("now2为", now2)

}

输出:
当前年月日为:2023-11-11 21:22:35
now2为 当前年月日为:2023-11-11 21:22:35

2)格式化第二种方法:使用 time.Format()方法完成
"2006/01/02 15:04:05"各个字符串的数字是固定的,必须这样写
各个数字可以自由组合,按实际需求返回时间日期

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Printf(now.Format("2006/01/02 15:04:05"))
	fmt.Println(" ")
	fmt.Printf(now.Format("2006-01-02"))
	fmt.Println(" ")
	fmt.Printf(now.Format("15:04:05"))
	fmt.Println(" ")

}

输出:
2023/11/11 21:43:24
2023-11-11
21:43:24

时间的常量
const(
Nanosecond Duration=1 纳秒
Microsecond=1000* Nanosecond Duration 微秒
Millisecond=1000Microsecond 微秒
Second=1000
Millisecond毫秒
Minute=60* Second 分钟
Hour=60* Minute小时
)

每隔0.1秒输出一个数字,打印到100时退出

func main() {
	i := 0
	for {
		i++
		fmt.Println(i)
		time.Sleep(time.Millisecond * 100)
		if i == 100 {
			break
		}
	}
}

获取当前unix时间戳和unixnano时间戳。(作用是可以获取随机数字)
使用方法:

func main() {
	now := time.Now()
	unix := now.Unix()
	fmt.Println(unix)
}

Golang内置函数
函数能被直接使用,称为内置函数。
1.len:用来求长度,比如string,array,slice,map,channel
2.new:用来分配内存,主要用来分配值类型,比如int,float,struct
3.make:用来分配内存,主要用来分配引用类型,比如chan,map,slice

Golang的错误机制
1.在默认情况下,程序发生错误,程序会自动退出(崩溃)
2.如果我们希望发生错误后,可以捕捉到错误,并进行处理,保证程序可以继续执行,还可以在捕捉到错误后,给管理员提示(邮件或者短信)
3.这里引出错误处理机制

基本说明
golang不支持try,catch,finally这种处理
golang中引入的处理方式,defer,panic,recover

使用defer+recover处理:

package main

import (
	"fmt"
)

func test() {
	defer func() {
		err := recover()	//内置函数,可以捕捉到错误信息
		if err != nil {		//说明捕捉到错误
			fmt.Println("err=", err)
		}
	}()
	num1 := 20
	num2 := 0
	num3 := num1 / num2
	fmt.Println(num3)
}

func main() {
	test()
	fmt.Println("继续运行")
}

自定义错误
Go程序中,也支持自定义错误,使用errors.New和panic内置函数
1.errors.New(错误说明),会返回error类型的值,表示一个错误
2.panic内置函数,接收一个interface类型的值(任何值)作为参数,接收error类型的变量,输出错误信息,并退出程序
案例示范:

package main

import (
	"errors"
	"fmt"
)
//函数去读取配置文件init.conf信息
//如果文件传入不正确,我就返回自定义错误信息
func readConf(name string) (err error) {
	if name == "config.ini" {
		return nil //没有错误
	} else {
		return errors.New("读取文件错误")
	}
}
func test2() {
	// err := readConf("ggg")//----------------这里影响输出结果
	err := readConf("config.ini")//----------------这里影响输出结果
	if err != nil {
		panic(err)
		//如果读取文件发生错误,就输出这个错误,并且终止程序
	}
	fmt.Println("test2继续执行")
}
func main() {
	test2()
	fmt.Println("下面代码继续运行")
}

练习题在:【尚硅谷】Golang入门到实战教程丨一套精通GO语言 P141

20.数组和切片

数组的定义:var a [3]int
数组的第一个元素是数组的首地址,第二个元素的地址是第一个元素地址+int

func main() {
	var a [3]int
	fmt.Println(a)
	fmt.Printf("a的地址是%p\n", &a)
	fmt.Printf("a第一个元素地址是%p\n", &a[0])
	fmt.Printf("a第二个元素地址是%p\n", &a[1])
}

输出:
[0 0 0]
a的地址是0xc00011e000
a第一个元素地址是0xc00011e000
a第二个元素地址是0xc00011e008

例题终端循环输入5个成绩,计算总和并输出各个成绩。

func main() {
	var score [5]float64
	for i := 0; i < len(score); i++ {
		fmt.Printf("请输入第%d个成绩", i+1)
		fmt.Scanln(&score[i])
	}
	TotalScore := 0.0
	for i := 0; i < len(score); i++ {
		TotalScore += score[i]
		fmt.Printf("第%d个成绩为%v\n", i+1, score[i])

	}
	fmt.Printf("总成绩为%v\n", TotalScore)
}

四种初始化数组的方式

func main() {
	var score [3]int = [3]int{1, 6, 9}
	fmt.Println("score的值为", score)
	var score1 = [3]int{2, 4, 6}
	fmt.Println("score1的值为", score1)
	var score2 = [...]int{3, 5, 7, 10}
	fmt.Println("score2的值为", score2)
	//指定元素对应的下标
	var score3 = [3]string{1: "hi", 0: "hello", 2: "ok"}
	fmt.Println("score3的值为", score3)
}

输出:
score的值为 [1 6 9]
score1的值为 [2 4 6]
score2的值为 [3 5 7 10]
score3的值为 [hello hi ok]

数组的遍历

for-range结构遍历
1.第一个index是数组的下标
2.第二个value是在该下标位置的值
3.他们都是仅在for循环内部可见的局部变量
4.遍历数组元素的时候,如果不行使用下标index,可以直接把下标index标为下划线_
5.index和value的名称不是固定的,即可自命名

func main() {
	var a = [...]int{1, 3, 5}
	for index, value := range a {
		fmt.Printf("index=%v,vaule=%v\n", index, value)
	}
	var b = [...]int{2, 4, 6}
	for _, value := range b {
		fmt.Printf("vaule=%v\n", value)
	}
}

输出:
index=0,vaule=1
index=1,vaule=3
index=2,vaule=5
vaule=2
vaule=4
vaule=6

数组注意事项和细节
1.数组是多个相同类型的数据组合,一个数组一旦声明了,其长度是固定的,不能动态变化
2.var arr []list,这时arr就是slice切片
3.数组中的元素可以是任何数据类型,包括值和引用类型。但不能混用
4.数组创建后,如果没有赋值,等于默认值(零值)
5.使用数组的步骤,1.声明数组并开辟空间2.给数组各个元素赋值3.使用数组
6.数组下标从0开始
7.数组下标必须在指定范围内使用,否则报panic
8.Go数组类型,在默认情况下是值传递。因此会进行值copy。数组间不会相互影响
9.若想在其他函数中,修改原来的值,可以进行引用传递(指针方式)

例题:
1.比较数组里面元素的大小

func main() {
	var a = [...]int{1, 3, 5}
	var max = a[0]
	for i := 0; i < len(a); i++ {
		if max < a[i] {
			max = a[i]
		}
	}
	fmt.Printf("max=%v", max)

}

2.用range数组和的平均值

func main() {
	var a = [...]int{1, 3, 5}
	sum := 0
	for _, value := range a {
		sum += value
	}
	fmt.Printf("sum=%v\n", sum)
	avg := sum / len(a)
	fmt.Printf("avg=%v", avg)
}

输出:
sum=9
avg=3

切片的基本介绍
1.切片英文叫slice
2.切片是数组一个引用,因此切片是引用类型。在进行传递时,遵守引用的传递机制
3.切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度也一样。
4.切片长度是可以变化的,因此切片是一个可以动态变化数组
5.切片定义的基本语法
var 变量名[] 类型,比如var a[] int

切片的使用

package main

import (
	"fmt"
)

func main() {
	var a [5]int = [...]int{1, 3, 5, 7, 9}

	slice := a[1:3]
	fmt.Println("a元素是", a)
	fmt.Println("slice元素是", slice)
	fmt.Println("slice长度是", len(slice))
	fmt.Println("slice容量是", cap(slice))
}

输出:
a元素是 [1 3 5 7 9]
slice元素是 [3 5]
slice长度是 2
slice容量是 4

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值