Go语言核心编程(二)

指针

基本介绍:

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

指针细节说明:

  • 值类型,都有对应的指针类型,形式为 *数据类型,比如int的对应的指针就是 *int,float32对应的指针类型就是 *float,依次类推
  • 值类型包括:基本数据类型 int 系列,float系列,bool,string、数组和结构体struct
值类型和引用类型

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

值类型和引用类型使用特点:
1)值类型:变量直接存储值,内存通常在栈中分配
2)引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收

标识符的命名规范

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

标识符的命名规则:

  • 由26个英文字母大小写,0-9,_组成
  • 数字不可以开头
  • Golang中严格区分大小写
  • 标识符不能包含空格
  • 下划线“_”本身在Go中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但是它对应的值会被忽略(比如,忽略某个返回值)。所以仅能被作为占位符使用,不能作为标识符使用
  • 不能以系统保留关键字作为标识符,比如break,if等等…

保留关键字介绍:

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

注:在Go中,如果变量名、函数名、常量名首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用(可以理解为,首字母大写是公开的,首字母小写是私有的),在golang中没有public,private等关键字。

预定义标识符:
除了保留关键字外,Go还提供了36个预定的标识符,其包括基础数据类型和系统内嵌函数

appendboolbytecapclosecomplex
complex64complex128uint16copyfalsefloat32
float64imagintint8int16uint32
int32int64iotalenmakenew
nilpanicuint64printprintlnreal
recoverstringtrueuintuint8uintprt
运算符

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等

  • 算术运算符
  • 赋值运算符
  • 比较运算符
  • 逻辑运算符
  • 位运算符
  • 其它运算符

注:Golang的++和–只能写在变量的后面不能写在变量的前面,即:只有a++,a–没有++a,–a;而且++和–只能独立使用。

逻辑运算符:
注意事项和细节说明:
1)&&也叫短路与:如果第一个条件为false,则第二个条件不会判断,最终结果为false
2)| | 也叫短路或:如果第一个条件为true,则第二个条件不会判断,最终结果为true

位运算符
运算符描述
&按位与运算符“&”是双目运算符,其功能是参与运算的两数各对应的二进位相与。运算规则是:同时为1,结果为1,否则为0
|按位或运算符,其功能是参与运算的两数各对应的二进位相或。运算规则是:有一个为1,结果为1,否则为0
^按位异或运算符,其功能是参与运算的两数各对应的二进位相异或,运算规则是:当二进位不同时,结果为1,否则为0
<<左移运算符,其功能是把"<<"左边的运算数的各二进位全部左移若干位,高位丢弃,低位补0,左移n位就是乘以2的n次方
>>右移运算符,其功能是把">>"左边的运算数的各二进位全部右移若干位,右移n位就是除以2的n次方

注意:Go语言不支持传统的三元运算符,要用if-else分支来实现。

键盘输入语句

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。
步骤:
1)导入fmt包;
2)调用fmt包的fmt.Scanln()或者fmt.Scanf()
Scanln()是程序会停止在这里,等待用户输入,并回车;
Scanf()是可以按指定的格式输入。

var name string
var age byte
var salary float32
var ispass bool
fmt.Println("请输入姓名:")
fmt.Scanln(&name)
...
fmt.Scanf("%s %d %f %t",&name,&age,&salary,&ispass)
原码、反码、补码

对于有符号的而言:

  • 二进制的最高位是符号位:0表示正数,1表示负数;
  • 正数的原码、反码和补码都一样;
  • 负数的反码=它的原码符号位不变,其他位取反;
  • 负数的补码=它的反码+1;
  • 0的反码,补码都是0;
  • 在计算机运算的时候,都是以补码的方式运算的。
跳转控制语句goto
  1. Go语言的goto语句可以无条件地转移到程序中指定的行;
  2. goto语句通常与条件语句配合使用,可用来实现条件转移,跳出循环体等功能;
  3. 在Go程序设计中一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序都产生困难。
fun main(){
	fmt.Println("ok1")
	fmt.Println("ok2")
	goto label
	fmt.Println("ok3")
	fmt.Println("ok4")
	label:
	fmt.Println("ok5")
}

函数注意事项:

  • 在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用;
  • 函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用!
  • 为了简化数据类型定义,Go支持自定义数据类型;
  • 支持对函数返回值命名
  • Go支持可变参数
func cal(n1 int,n2 int)(sum int,sub int){
		sum = n1 + n2
		sub = n1 - n2
		return
}
func sum(args...int)sum int{
}
func sum(n1 int,args...int)sum int{
}
init函数

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。
1)如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义——>init函数——>main函数
2)init函数最主要的作用是,就是完成一些初始化工作。

匿名函数

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

  • 匿名函数使用方式1
    在定义匿名函数时就是使用
func main(){
	res := func(n1 int,n2 int) int{
		return n1 + n2
}(10,20)
  • 匿名函数使用方式2
    将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
func main(){
	res := func(n1 int,n2 int) int{
		return n1 + n2
}
a := res(10,20)

全局匿名函数:
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序中有效。

闭包

基本介绍:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
闭包最佳实践:
请编写一个程序,具体要求如下:
1)编写一个函数makeSuffix(suffix string)可以接收一个文件后缀名(比如.jpg)并返回一个闭包;
2)调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回原文件名;
3)要求使用闭包的方式完成;
4)strings.HasSuffix.
在这里插入图片描述
代码说明:
1)返回的函数和makeSuffix(suffix string)的suffix变量和返回的函数组合成一个闭包,因为返回的函数引用到suffix这个变量;
2)闭包的好处:如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入后缀名,比如.jpg,而闭包因为可以保留上次引用的某个值,所以传入一次就可以反复使用。
在这里插入图片描述

函数中-defer

为什么需要defer?
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等)为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)
在这里插入图片描述
程序输出的结果顺序是:
1)res=30
2)ok2 n2=20
3)ok1 n1=10
当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈),当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行。

defer细节说明:
1)当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一语句;
2)当函数执行完毕后,在从defer栈中,依次从栈顶中取出语句执行;
3)在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。
注:defer之后相关变量仍然可以继续使用。(defer file.close() defer connect.close())

函数参数的传递方式

两种传递方式:
1)值传递
2)引用传递
其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝。一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。

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

变量作用域

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

字符串中常用的函数

说明:字符串在我们程序开发中,使用的是非常多的“:
1)统计字符串的长度,按字节len(str)
2)字符串遍历,同时处理有中文的问题r:=[]rune(str)
3)字符串转整数:n,err:=strconv.Atoi(“12”)
4)整数转字符串:str=strconv.Itoa(12345)
5)字符串转[]byte:var bytes=[]byte{“hello go”}
6)[]byte转字符串:str=string([]byte{97,98,99})
7)10进制转2,8,16进制:str=strconv.FormatInt(123,2)
8)查找子串是否在指定的字符串中:strings.Contains(“seafood”,“foo”)
9)统计一个字符串有几个指定的子串:strings.Count(“ceheese”,“e”)
10)不区分大小写的字符串比较(==是区分字母大小写的):fmt.Println(strings.EqualFold(“abc”,“Abc”))
11)返回子串在字符串第一次出现的index值,如果没有返回-1:strings.Index(“NIL_abc”,“abc”)
12)返回子串在字符串最后一次出现的index,如没有返回-1:strings.LastIndex[“go golang”,“go”]
13)将指定的子串替换成另外一个子串:strings.Replace(“go go hello”,“go”,“go语言”,n)n可以指定你希望替换几个,如果n=1表示全部替换
14)按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组
15)将字符串的字母进行大小写的转换:strings.ToLower(“Go”)
16)将字符串左右两边的空格去掉:strings.TrimSpace(" to huhj jioj “)
17)将字符串左右两边指定的字符去掉:strings.Trim(” ! hello!","!")
18)将字符串左边指定的字符去掉:strings.TrimLeft("! hello !","!")
19)将字符串右边指定的字符q去掉:strings.TrimRight("! hello !","!")
20)判断字符串是否以指定的字符串开头:strings.HasPrefix(“ftp://192.168.10.1”,“ftp”)
21)判断字符串是否以指定的字符串结束:strings.HasSuffix(“NIL_abc.jpg”,“abc”)

… …
查看官方文档:https://studygolang.com/pkgdoc

Go错误处理机制

基本说明:
1)Go语言追求简洁优雅,所以,Go语言不支持传统的try…catch…finally这种处理;
2)Go中引入的处理方式为:defer,panic,recover
3)这几个异常的使用场景可以简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
在这里插入图片描述

数组和切片

四种初始化数组的方式:

  1. var num [3]int = [3]int{1,2,3}
  2. var num = [3]int{1,2,3}
  3. var num = […]int{1,2,3}
  4. var num = […]int{1:99,0:100,2:9}

数组的遍历:
1.常规遍历
2.for-range遍历
for index,value := range array{
}

切片——golang中一种特有的数据类型

为什么需要切片?
比如我们需要一个数组用于保存学生的成绩,但是学生的个数是不确定的,就可以使用切片来解决。
基本介绍:
1)切片——slice
2)切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
3)切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样
4)切片的长度是可以变化的,因此切片是一个可以动态变化的数组
5)切片定义的基本语法:
var 变量名 []类型
比如:var a []int

type slice struct{
ptr *[2]int
len int
cap int
}
切片的使用:
1.定义一个切片,然后让切片去引用一个已经创建好的数组;
2.通过make来创建切片。
基本语法:
var 切片名[] type = make([],len,[cap])
参数说明:type就是数据类型 len:大小 cap:指定切片容量(即最大可以存放多少个元素),可选
3)定义一个切片,直接就指定具体数组,使用原理类似make的方式:var num []int=[]int{1,2,3}

方式1和方式2的区别:
方式1是直接引用数组,这个数组是事先存在的,程序员是可见的;
方式2是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的。
在这里插入图片描述
注:切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。

string和slice

1)string底层是一个byte数组,因此string也可以进行切片处理
2)string和切片在内存的形式
3)string是不可变的 ,也就是说不能通过str[0]='z’方式来修改字符串
4)如果需要修改字符串,可以先将string——>[]byte修改重写转成string

排序的介绍

排序的分类:
1)内部排序:
指将需要处理的所有数据都加载到内部存储器中进行排序:
包括交换式排序法、选择式排序法、插入式排序法
2)外部排序法:
数据量过大,无法全部加载到内存中,需要借助外部存储进行:
包括合并排序法和直接合并排序法

交换式排序法

交换式排序属于内部排序法,是运用数据值比较后,依判断规则对数据位置进行交换,以达到排序的目的。
交换式排序法又可分为两种:
1)冒泡排序法
2)快速排序法

查找

在golang中,我们常用的查找有两种:
1)顺序查找;
2)二分查找(该数组是有序的);

二维数组的使用

使用方式:先声明/定义再赋值
1)语法:var 数组名[大小][大小]类型
2)比如:var arr [2][3]int,再赋值
3)二维数组在内存的存在形式(重点)

使用方式2:直接初始化
1)声明:var 数组名[大小][大小]类型=[大小][大小]类型{{初值…},{初值…}}
2)赋值(有默认值,比如int 类型的就是0)
3)说明:二维数组在声明/定义时也对应有四种写法[和一维数组类似]

二维数组的遍历:
1)双层for循环完成遍历
2)for-range方式完成遍历

map

map介绍:
map是key-value数据结构,又称为字段或者关联数组,类似其它编程语言的集合,在编程中是经常用到。
基本语法:
var map变量名 map[keytype]valuetype
key可以是什么类型?
golang中的map的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是只包含前面几个类型的接口、结构体、数组
通常为int,string

注意:slice,map还有function不可以,因为这几个没法用 == 来判断

map声明的简单举例:
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string
注意:声明是不会分配内存的,初始化需要make,分配内存后才能赋值和使用。

func main(){
	var a map[string]string
	a = make(map[string]string,10)
	a["no1"] = "宋江"
	fmt.Println(a)
}

或者在声明的时候直接赋值:

func main(){
	var a map[string]string = map[string]string(
	"NO1":"宋江",
	)
	a["No2"] = "武松"
}

map遍历:
说明:map的遍历使用for-range的结构遍历
map切片:
切片的数据类型如果是map,则我们称为slice of map,map切片,这样使用则map个数就可以动态变化了。

func main(){
	var monsters []map[string]string
	monsters = make([]map[string]string,2)
	if monsters[0] == nil{
		monsters[0] = make(map[string]string,2)
		monsters[0]["name"] = "monster1"
		monsters[0]["age"] = "500"
	}
}

下面用到切片的append函数,可以动态的增加monster

newMonster := map[string]string(
	"name":"monster2",
	"age":1000,
	)
newMonster = append(monsters,newMonster)

map排序:
1)golang中没有一个专门的方法针对map的key进行排序
2)golang中的map默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能不一样
3)golang中map的排序,是先将key进行排序,然后根据key值遍历输出即可

如果按照map的key的顺序进行排序输出:
1.先将map的key放到切片中
2.对切片排序
3.遍历切片,然后按照key来输出map

var keys []int
for k,_ :range map1{
	keys = append(keys,k)
}
#排序
sort.Intskeys(keys)
for _,k :range keys{
	fmt.Println(k,map1[k])
}

map使用细节:
1)map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map
2)map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长键值对(key-value)
3)map的value也经常使用struct类型,更适合管理复杂的数据,比如value为Student结构体
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Despacito1006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值