Golang 基础(变量、数据类型)

Golang 基础 Golang basic knowledge

1 变量 variables

1.1 声明变量

Golang 标准的声明变量格式为:

var <name> <type>

其中,var是关键字,name是变量名,type是类型,将var替换为const则声明常量.

Go 会对变量进行隐式初始化,比如string类型会初始化为空字符串,int 类型就初始化为0,float 就初始化为 0.0,bool类型就初始化为false,指针类型就初始化为 nil。

在声明过程中,可以对变量初始化

var name string = "vancode golang"

从右值(rvalue)来看,该变量明显是string类型,因此可以简写为:

var name = "vancode golang"

与python不同,go中双引号表示字符串,而单引号表示rune类型的字符

若右值(rvalue)为浮点型,在不指定类型的情况下,编译器会将你的这个变量声明为 float64,但是很多情况下,我们并不需要这么高的精度(占用的内存空间更大).

故,推荐指定类型:

var rate float32 = 0.89

1.2 多变量声明

声明多个变量可以使用如下语法

var (
	name string = "linhao"
	age  int    = 123
)

同样可以使用const替代var声明多个常量

const (
	NAME string = "LINHAO"
	AGE  int    = 12
)

1.3 推导声明写法

使用:=声明变量,并对其进行初始化

推导声明写法或者短类型声明法,编译器会自动根据右值类型推断出左值对应的类型.

name := "Vance"
// 等价于
var name string = "Vance"
// 等价于
var name = "Vance"

注意:

该方法只能用于函数内部

func printVar() {
	name := "Vance"
	fmt.Println(name)
}

1.4 声明和初始化多个变量

name,age := "Vance",24

1.5 new 函数声明一个指针变量

使用new(Type)将创建一个Type类型的匿名变量,初始化为Type类型的0值,然后返回变量地址,返回的指针类型为*Type.

	ptr := new(int)
	fmt.Println(ptr)
	fmt.Println(*ptr)

new() 是一种语法糖,用new创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(Type)

如下两种写法等价

func ptrInt() *int{
	return new(int)
}
func ptrIntNew() *int{
	var tempInt int
	return &tempInt
}

1.6 匿名变量

匿名变量通常用来接受必须接受但是无用的值,用_表示

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

2 数据类型 The data type

2.1 整型

存储

整形分为有符号整型int和无符号整型uint

以 8位整型为例

uint是无符号,表示的是正数,范围为0-255

int是有符号,表示范围-128-127

  • 当你在32位的系统下,intuint 都占用 4个字节,也就是32位
  • 若你在64位的系统下,intuint 都占用 8个字节,也就是64位
func SizeIntUnit() {
	var intVar int
	var uintVar uint
	fmt.Printf("intVar 数据类型为 %T,大小为 %d\n", intVar, unsafe.Sizeof(intVar))
	fmt.Printf("uintVar 数据类型为 %T,大小为 %d\n", uintVar, unsafe.Sizeof(uintVar))
}

✔️ output

intVar 数据类型为 int,大小为 8
uintVar 数据类型为 uint,大小为 8
进制表示
func PrintBinary() {
	num := 0b1100
	fmt.Printf("二进制数%b,十进制数%d\n", num, num)
}
func PrintOctal() {
	num := 0o1100
	fmt.Printf("八进制数%o,十进制数%d\n", num, num)
}
func PrintHexadecimal() {
	num := 0x1100
	fmt.Printf("十六进制数%X,十进制数%d\n", num, num)
}

fmt包的格式化输出功能,参考如下

%b 表示为二进制
%c 该值对应的unicode码值
%d 表示为十进制
%o 表示为八进制
%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x 表示为十六进制,使用a-f
%X 表示为十六进制,使用A-F
%U 表示为Unicode格式:U+1234,等价于"U+%04X"
%E 用科学计数法表示
%f 用浮点数表示

2.2 浮点型

Go语言中提供了两种精度的浮点数 float32float64

float32,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号,8位用来指数,剩下的23位表示尾数

float64,也即我们熟悉的双精度,存储占用8个字节,也即8*8=64位,其中1位用来符号,11位用来指数,剩下的52位表示尾数

func PrintMax_Min() {
	fmt.Printf("MaxFloat32=%E\n", math.MaxFloat32)
	fmt.Printf("MaxFloat64=%E\n", math.MaxFloat64)
}

✔️ output

MaxFloat32=3.402823E+38
MaxFloat64=1.797693E+308

2.3 布尔型

Go 语言中,没有布尔型和整型的转换,需要手动转换

func TestBool() {
	btrue := true
	bfalse := false
	fmt.Println()
	// bool 转 int
	bool2Int := func(b bool) int {
		if b {
			return 1
		}
		return 0
	}
	fmt.Println(bool2Int(btrue))
	fmt.Println(bool2Int(bfalse))
	// int 转bool
	int2Bool := func(i int) bool {
		return i != 0
	}
	fmt.Println(int2Bool(1))
	fmt.Println(int2Bool(0))
}

✔️ output

1
0
true
false

2.4 byte rune 和字符串

byte 和 rune
  • byteuint8类型本质上没有区别,占用1个字节\8位,表示范围0~255,表示的是 ACSII 表中的一个字符
  • runeint32类型本质上没有区别,占用4个字节\32位,表示的是一个 Unicode字符
func PrintByteUint() {
	var a byte = 65
	var b uint = 66
	var a2 byte = 'A'
	var b2 uint = 'B'
	fmt.Printf("a = %c, b = %c\n", a, b)
	fmt.Printf("a2 = %c, b2 = %c\n", a2, b2)
}

✔️ output

a = A, b = B
a2 = A, b2 = B
  • 在 Go 中单引号与 双引号并不是等价的,

  • uint8 和 int32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。

string
  • 字符串使用双引号和反引号表示
  • 双引号的字符串带转义字符,而反引号的字符串忽略转义(相当于Python中的raw字符串)
  • 双引号字符串,打印时可以使用fmt%q来忽略转移
  • 反引号字符串可以直接在代码中换行来表示字符串换行
func PrintString() {
	var str1 string = "hello \\world"
	var str2 string = `hello \world`
	fmt.Println(str1)
	fmt.Println(str2)
	fmt.Printf("%q\n", str1)
	fmt.Print(`hello
world`)
}

✔️ output

hello \world
hello \world
"hello \\world"
hello
world

2.5 数组

1 声明

Go 语言的数组可以直接按var <name> [<size>]<type> 声明后对单一元素进行赋值,也可以在声明时直接`进行赋值,也可以直接使用赋值对数组声明创建数组变量

func ListStatement() {
	// 声明
	var arr1 [3]int
	// 声明并初始化
	var arr2 [3]int = [3]int{1, 2, 3}
	// 初始化声明
	arr3 := [3]int{1, 2, 3}

	fmt.Println(arr1)
	fmt.Println(arr2)
	fmt.Println(arr3)
}

✔️ output

[0 0 0]
[1 2 3]
[1 2 3]
2 自动分配空间

Go 语言中,数组可以使用...根据赋值自动分配数组空间

// 数组自动分配空间
func ListAutoSize() {
	arr := [...]int{1, 2, 3}
	fmt.Println(arr)
	fmt.Println(len(arr))
}

✔️ output

[1 2 3]
3
3 数组类型

不同size的数组属于不同的变量类型

  • 可以通过fmt%T和反射获取变量类型.
// 数组类型
func ListType() {

	arr1 := [...]int{1, 2, 3}
	arr2 := [...]int{1, 2, 3, 4}
	fmt.Println()
	// %T 输出变量类型
	fmt.Printf("%T\n", arr1)
	fmt.Printf("%T\n", arr2)
	// 反射输出变量类型
	fmt.Println(reflect.TypeOf(arr1))
	fmt.Println(reflect.TypeOf(arr2))
}

✔️ output

[3]int
[4]int
[3]int
[4]int
4 类型别名

使用 type 关键字可以定义一个类型字面量,后面只要你想定义一个指定容器,元素类型的数组 ,都可以使用这个别名类型。

func ListTypeAlias() {
	type arrType [3]int
	myarr := arrType{1, 2, 3}
	fmt.Println()
	fmt.Println(myarr)
	fmt.Println(reflect.TypeOf(myarr))
}

✔️ output

[1 2 3]
list.arrType
5 按索引初始化

在创建时数组时,可以使用指定索引值进行初始化

func ListIndex() {
	arr := [4]int{2: 3}
	fmt.Println()
	fmt.Println(arr)
}

✔️ output

[0 0 3 0]

2.6 切片

切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。

切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间)

关于切片构造,有四种方式:

1 对数组进行片段截取
func SliceCutfromList() {
	list := [8]int{1, 2, 3, 4, 5, 6, 7, 8}
	// 表示截取数组list的从索引1开始,到索引2(3-1)截至的元素
	slice1 := list[1:3]
	// 设置切片容量的截止索引
	slice2 := list[1:3:4]
	fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
	fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
}

✔️ output

切片 slice1: [2 3] 的长度为: 2,容量为: 7
切片 slice2: [2 3] 的长度为: 2,容量为: 3
2 声明并赋值
func SliceStatement() {
	var slice1 []int
	slice1 = append(slice1, 123)
	var slice2 []int = []int{1, 2, 3, 4}
	slice3 := []int{1, 2, 3, 4}
	fmt.Println()
	fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
	fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
	fmt.Printf("切片 slice3: %d 的长度为: %d,容量为: %d \n", slice3, len(slice3), cap(slice3))
}

✔️ output

切片 slice1: [123] 的长度为: 1,容量为: 1
切片 slice2: [1 2 3 4] 的长度为: 4,容量为: 4
切片 slice3: [1 2 3 4] 的长度为: 4,容量为: 4
3 使用make构造切片

使用make函数构造切片,格式如下:

make( []Type, size, cap )
func SliceMake() {
	slice1 := make([]int, 2)
	slice2 := make([]int, 2, 6)
	fmt.Println()
	fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
	fmt.Printf("切片 slice2: %d 的长度为: %d,容量为: %d \n", slice2, len(slice2), cap(slice2))
}

✔️ output

切片 slice1: [0 0] 的长度为: 2,容量为: 2
切片 slice2: [0 0] 的长度为: 2,容量为: 6
4 指定索引值构造切片
// 指定索引对应值构造
func SliceIndex() {
	slice1 := []int{2: 13}
	fmt.Println()
	fmt.Printf("切片 slice1: %d 的长度为: %d,容量为: %d \n", slice1, len(slice1), cap(slice1))
}

✔️ output

切片 slice1: [0 0 13] 的长度为: 3,容量为: 3

2.7 字典

1 字典声明

以下是三种字典声明方式,注意,Go中,字典需要指定key,value的数据类型.

func DictStatement() {
	// 声明并初始化
	var dict1 map[string]int = map[string]int{"age": 16, "year": 2022}
	dict2 := map[string]int{"age": 16, "year": 2022}
	dict3 := make(map[string]int)
	dict3["age"] = 16
	dict3["year"] = 2022
	fmt.Println(dict1)
	fmt.Println(dict2)
	fmt.Println(dict3)
}

✔️ output

map[age:16 year:2022]
map[age:16 year:2022]
map[age:16 year:2022]
2 字典相关操作

添加\更新元素

使用dict[key]=value直接修改字典值,如果字典没有该key则新增元素

// 字典相关操作
func DictOper() {
	dict1 := map[string]int{"age": 16, "year": 2022}
	// 添加,更新元素
	dict1["month"] = 10
	dict1["year"] = 2021
	fmt.Println()
	fmt.Println(dict1)
}

✔️ output

map[age:16 year:2022]
map[age:16 year:2022]
map[age:16 year:2022]

读取元素

如果字典中没有该key,会返回该数据类型的0值.

	fmt.Println(dict1["month"])
	fmt.Println(dict1["day"])

✔️ output

10
0

删除元素

删除元素,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。

	// 删除元素
	delete(dict1,"month")
	fmt.Println(dict1)

✔️ output

map[age:16 year:2021]
3 字典判断key存在

字典下标读取某key的值时,不管有没有该key,都会有返回值(如果没有该key,返回该数据类型的0值),所以不能靠返回的结果判断有没有key,但是字典按下标读取时,会有两个返回值,第二个返回值为bool型表示key是否存在

// 判断key 存在
func DictKeyExists() {
	dict1 := map[string]int{"age": 17, "year": 2022}
	checkKeyExists := func(dict map[string]int, key string) {
		if value, exist := dict[key]; exist {
			fmt.Printf("key: %s 存在,值为: %d\n", key, value)
		} else {
			fmt.Printf("key: %s 不存在\n", key)
		}
	}
	checkKeyExists(dict1, "month")
	checkKeyExists(dict1, "year")
}

✔️ output

key: month 不存在
key: year 存在,值为: 2022
4 字典遍历

Go中可以使用range对字典的key,value进行遍历

// 字典遍历
func DictRange() {
	dict1 := map[string]int{"age": 16, "year": 2022, "month": 2, "day": 1, "hour": 12}
	fmt.Println()
	for key, value := range dict1 {
		fmt.Printf("key:%s,value:%d\n", key, value)
	}
	for key := range dict1 {
		fmt.Printf("key:%s\n", key)
	}
	for _, value := range dict1 {
		fmt.Printf("value:%d\n", value)
	}
}

✔️ output

key:month,value:2
key:day,value:1
key:hour,value:12
key:age,value:16
key:year,value:2022
key:age
key:year
key:month
key:day
key:hour
value:12
value:16
value:2022
value:2
value:1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值