值类型和引用类型
值类型
int、float、bool、string、数组、结构体
变量直接存储值,内存通常在栈中分配
引用类型
指针、slice切片、map、管道chan、interface
变量存储的是一个地址,这个地址对应的空间才是真正的存储数据(值),内存通常是在堆中分配的,没有任何变量引用这个地址时,改地址对应的数据空间就成为了一个垃圾,由GC来回收。
基本数据类型:
在Go中数据类型都有一个默认值,没有赋值时就会保留默认值,也叫零值。
数值型
Golang程序中整数类型变量在使用时,遵守保小不保大的原则,即在保证程序正确运行的情况下,尽量使用占用空间小的数据类型
整数类型:
有符号整数
无符号整数
package mainimport ("fmt""unsafe") func main(){var n int = 10//unsafe.Sizeof(n) 可以返回变量占用的字节数fmt.Printf("n 的类型 %T n占用的字节数是%d",n, unsafe.Sizeof(n))}
浮点类型:
可以用来表示小数
浮点数 = 符号位 + 指数位 + 尾数位
尾数位可能丢失造成精度损失
字符型
没有专门的字符型,使用byte来保存单个字母字符,由字符组成
注意:不能用byte存汉字,在go中采用utf-8的编码,一个汉字会占3个字节,可以用int32或者rune来储存
package mainimport ("fmt") func main(){//字符类型可以用来计算,相当于一个整数,数值为码值var n = 10 + 'a'fmt.Println(n) //107}
布尔型(bool)
只能取true
和false
占用一个字节
适用于逻辑运算
字符串(string)
是由固定长度的字符串联起来的字符序列,Go的字符是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
使用形式:
1、双引号,会识别转义字符。
2、反引号,以字符串的原生形态输出保持换行和特殊字符,可以实现防止攻击。
注意:
1、字符串一旦赋值了,就不能修改了。
2、字符串拼接使用+
基本类型的类型转换
基本语法 T 将值v的类型转换为T
注意:
1、被转换的是变量存储的数据,变量本身的数据类型没有变化。
2、在转换中,比如int64转成int8,编译时不会报错,只是转换的结果会按溢出处理,和我们希望的结果不一样。
package mainimport ("fmt""unsafe") func main(){var i int = 1var j float32 = float32(i)fmt.Printf("i 的类型 %T n占用的字节数是%d \n" ,i, unsafe.Sizeof(i))fmt.Printf("j 的类型 %T n占用的字节数是%d \n" ,j, unsafe.Sizeof(j))}
派生/复杂数据类型:
指针(Pointer)
指针变量存的是一个地址,这个地址指向的内存空间才是值
值类型都有对应的指针类型,形式为*指针类型
值类型包括:int 、float 、 bool、string、数组、结构体
package mainimport ("fmt") func main(){var a int = 1fmt.Printf("a的地址%v\n",&a)var b *int = &afmt.Printf("b的值%v\n",b)fmt.Printf("b的地址%v\n",&b)fmt.Printf("b指向的值%v\n",*b)}
利用指针改变值
package mainimport ("fmt") func main(){var a int = 1var b *int = &a*b = 2fmt.Printf("a的值%v",a)}
数组
基本用法
语法var variable_name [SIZE] variable_type
package mainimport ("fmt") func main(){arr := [6] int {1,2,3,4,5,6} //int 占8字节fmt.Printf("arr的地址是%p \n", &arr) fmt.Printf("arr[0]的地址是%p \n", &arr[0]) //数组中第一个元素的地址和数组变量的地址是一样的fmt.Printf("arr[1]的地址是%p \n", &arr[1]) //后面元素的地址就是在数组地址基础上加上占用字节数// arr的地址是0xc00000a300 // arr[0]的地址是0xc00000a300 // arr[1]的地址是0xc00000a308}
如果数组长度不确定,可以使用 … 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var arr = [...]float32{1,2,3,4,5,6}
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
arr := [5]float32{1:2.0,3:7.0}
注意事项
1、一旦声明,其长度就不能改变
2、数组创建后,如果没有赋值,有默认值(零值):
int–> 0
bool–>false
string–> “”
3、数组中的元素可以是任意类型
结构体(struct)
管道(Channel)
函数
为完成某一功能的程序指令(语句)的集合,称为函数。
函数执行时会在内存中开辟一个执行空间,函数执行完毕该空间释放。
在Go中,函数分为:自定义函数、系统函数。
Go函数支持多个返回值。
func 函数名(形参列表)(返回值类型列表){执行语句...return 返回值列表}
函数的命名
1、函数的命名遵循标识符命名规格,首字母不能是数字
2、首字母大写的函数可以被本包和其他包文件使用,类似public
3、首字母小写只能被本包使用,其他包不能使用,类似于private
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内部修改,不会影响到原来的值。
如果希望函数内的变量能影响到函数外的变量,可以传入变量的地址&,函数内衣指针的方式操作变量。
package mainimport ("fmt") func test1( n int) {n++fmt.Printf("test1 a= %v\n",n)}func test2( n *int ) {*n++fmt.Printf("test2 b=%v\n",*n)}func main(){a:=1b:= 1test1(a)test2(&b)fmt.Printf("a=%v\nb=%v\n",a,b)}
在go中函数也是一种数据类型。
package mainimport ("fmt") func getSum( n1 int, n2 int) int {return n1 + n2}//函数是一种数据类型,因此可以作为形参,并且调用func sumFun(funvar func(int, int) int, num1 int, num2 int ) int {return funvar(num1,num2)}func main(){//可以复制给一个变量,该变量就是一个函数类型的变量了,通过该变量可以对函数调用。a:= getSumres1 := a(1,2)res2 := sumFun(a,3,4)fmt.Printf("res1 = %v \n res2 =%v \n",res1,res2)}
支持返回值命名
package mainimport ("fmt") func myfun(n1 int, n2 int) (sum int , sub int) {//sum,sub为默认返回值已声明,可以直接赋值sum = n1 + n2sub = n1 - n2return}func main(){a, b := myfun(20,10)c, _ := myfun(30, 40) // _代表占位符fmt.Printf("a = %v \nb =%v \nc =%v \n",a,b,c)}
支持可变参数
通过args[index]
获取参数
//支持0到多个参数func myfun(args...int) int{}//支持1到多个参数func myfun(n int,args...int) int{}
匿名函数
1、在定义匿名函数时直接调用,这种方式匿名函数只能调用一次
func main(){res := func (n1 int,n2 int) int {return n1 + n2}(1,2)fmt.Println("a=",res)}
2、将定义的匿名函数赋值给变量、这种方式可以多次调用
func main(){a := func (n1 int,n2 int) int {return n1 + n2}res := a(1,2)fmt.Println("res=",res)}
闭包
闭包就是一个函数和其他相关的引用环境组合的一个整体
package mainimport ("fmt") func add() func (int) int {n := 10return func (x int) int {return n + x}}func main(){f := add()res1 := f(2)res2 := f(3)fmt.Println("res=",res1,res2)}
defer
特性
关键字 defer 用于注册延迟调用。这些调用直到 return 前才被执。因此,可以用来做资源清理。多个defer语句,按先进后出的方式执行。defer语句中的变量,在defer声明时就决定了。
用途
关闭文件句柄锁资源释放数据库连接释放
当go执行到defer时,不会立即执行defer后的语句,而是将defer后的语句亚茹到一个栈中,然后执行下一语句,在函数执行完之后,再从栈中执行语句(先入后执行)
package mainimport ("fmt") func sum(n1 int, n2 int) int {//defer入栈时会拷贝相应的值defer fmt.Println("n1=",n1) //n1=10defer fmt.Println("n2=",n2) //n2=20n1++n2++res := n1 + n2defer fmt.Println("res=",res) //res=32return res}func main(){num := sum(10,20)fmt.Println("num=",num) //num=32}
切片(slice)
接口(interface)
map
自定义数据类型
基本语法: type 自定义数据类型名 数据类型
package mainimport ("fmt") func main(){//给int取了别名,在go中 myint 和 int 都是int类型,但是go认为他们是两个类型type myint intvar num1 myintvar num2 intnum1 = 40num2 = num1 //cannot use num1 (type myint) as type int in assignmentfmt.Printf("num1 = %v \num2 =%v \n",res1,res2)}
type myFunType func(int,int) int//函数是一种数据类型,因此可以作为形参,并且调用func sumFun(funvar myFunType, num1 int, num2 int ) int {return funvar(num1,num2)}