这是我参与「第三届青训营 -后端场」笔记创作活动的的第一篇笔
GO基础一变量声明
第一种 :一行一个变量,静态语言最基本常用的方式
var <name> <type>
其中 var 是关键字(固定不变),name 是变量名,type 是类型
使用 var ,虽然只指定了类型,但是 Go 会所有类型都有默认值,比如 string 类型会初始化为空字符串,int 类型会初始化为0,float 会初始化为 0.0,bool会初始化为false,引用和指针类型就初始化为 nil 等。
若要在声明时,顺便也初始化,可以这样写
var name sting = "第一个变量"
第二种:多个变量一起声明,声明组
声明多个变量,除了可以按照上面写成多行之外,还可以写成下面这样
var (
name string
age int
gender string
)
第三种:短声明,只能在函数内
使用 := (推导声明写法或短类型声明法:编译器会自动根据右值类型推断出左值的对应类型。),可以声明一个变量,并对其进行(显式)初始化。
name := "Go"
// 等价于
var name string = "GO"
// 等价于
var name = "GO"
第四种:通过 new 创建指针变量
先简单介绍下指针的相关内容。
一般变量分为两种 普通变量 和 指针变量
普通变量,存放的是数据本身,而指针变量存放的是数据的地址。
如下代码,age 是一个普通变量,存放的内容是 28,而 ptr 是 存放变量age值的内存地址:0xc000010098
package main
import "fmt"
func main() {
var age int = 28
var ptr = &age // &后面接变量名,表示取出该变量的内存地址
fmt.Println("age: ", age)
fmt.Println("ptr: ", ptr)
}
输出
age: 28
ptr: 0xc000010098
而这里要说的 new 函数,是 Go 里的一个内建函数。
使用表达式 new(Type) 将创建一个Type类型的匿名变量,初始化为Type类型的零值,然后返回变量地址,返回的指针类型为*Type。
package main
import "fmt"
func main() {
ptr := new(int)
fmt.Println("ptr address: ", ptr)
fmt.Println("ptr value: ", *ptr) // * 后面接指针变量,表示从内存地址中取出值
}
输出
ptr address: 0xc000010098
ptr value: 0
用new创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(Type)。换言之,new函数类似是一种语法糖,而不是一个新的基础概念。
如下两种写法,可以说是等价的
// 使用 new
func newInt() *int {
return new(int)
}
// 使用传统的方式
func newInt() *int {
var dummy int
return &dummy
}
第五种:make 函数创建 slice、map 或 chan 类型变量
var slice = make([]int, 8)var m = make(map[string]int)var c = make(chan int)
slice、map 和 chan 是 Go 中的引用类型,它们的创建和初始化,一般使用 make。特别的,chan 只能用 make。slice 和 map 还可以简单的方式:
slice := []int{0, 0}m := map[string]int{}
GO基础二数据类型byte ,rune,string的解析
. byte 与 rune
byte,占用1个节字,就 8 个比特位,所以它和 uint8 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。
如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b
import "fmt"
func main() {
var a byte = 65
// 8进制写法: var c byte = '\101' 其中 \ 是固定前缀
// 16进制写法: var c byte = '\x41' 其中 \x 是固定前缀
var b uint8 = 66
fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
// 或者使用 string 函数
// fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b))
}
在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 66,所以上面的代码也可以写成这样
import "fmt"
func main() {
var a byte = 'A'
var b uint8 = 'B'
fmt.Printf("a 的值: %c \nb 的值: %c", a, b)
}
他们的输出结果都是一样的。
a 的值: A
b 的值: B
rune,占用4个字节,共32位比特位,所以它和 uint32 本质上也没有区别。它表示的是一个 Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)。
import (
"fmt"
"unsafe"
)
func main() {
var a byte = 'A'
var b rune = 'B'
fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b))
}
输出如下
a 占用 1 个字节数
b 占用 4 个字节数
由于 byte 类型能表示的值是有限,只有 2^8=256 个。所以如果你想表示中文的话,你只能使用 rune 类型。
var name rune = '中'
或许你已经发现,上面我们在定义字符时,不管是 byte 还是 rune ,我都是使用单引号,而没使用双引号。
对于从 Python 转过来的人,这里一定要注意了,在 Go 中单引号与 双引号并不是等价的。
单引号用来表示字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。
cannot use "A" (type string) as type byte in assignment
上面我说了,byte 和 uint8 没有区别,rune 和 uint32 没有区别,那为什么还要多出 byte 和 rune 类型呢?多乱呀。
理由很简单,因为uint8 和 uint32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。
2. 字符串
字符串,可以说是大家很熟悉的数据类型之一。定义方法很简单
var mystr string = "hello"
上面说的byte 和 rune 都是字符类型,若多个字符放在一起,就组成了字符串,也就是这里要说的 string 类型。
比如 hello ,对照 ASCII 编码表,每个字母对应的编号是:104,101,108,108,111
import (
"fmt"
)
func main() {
var mystr01 sting = "hello"
var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111}
fmt.Printf("mystr01: %s\n", mystr01)
fmt.Printf("mystr02: %s", mystr02)
}
输出如下,mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 byte数组
mystr01: hello
mystr02: hello
通过以上学习,我们知道字符分为 byte 和 rune,占用的大小不同。
这里来考一下大家,hello,中国 占用几个字节?
要回答这个问题,你得知道 Go 语言的 string 是用 uft-8 进行编码的,英文字母占用一个字节,而中文字母占用 3个字节,所以 hello,中国 的长度为 5+1+(3*2)= 12个字节。
import (
"fmt"
)
func main() {
var country string = "hello,中国"
fmt.Println(len(country))
}
// 输出
12