目录
【1】变量
go语言是强类型语言,但是声明变量时也不是必须明确数据类型,如果不声明数据类型,go会自动判断这个变量的数据类型是什么。
package main
import "fmt"
//设置全局变量
var s1 = 10
var s2 = 11
//一次性定义全局变量
var (
s3 = "你好"
s4 = 10.99
)
func main(){
//在函数里面声明的变量为局部变量
//声明一个变量并设置数据类型
var age int
//给变量赋值
age = 18
//声明一个变量不设置数据类型(自动类型推断)
var num = "99.1"
//省略var 使用 := 代替 =
str := "hello"
//声明多个变量
num1,str2,num3 := 12,"nihao",1.15
}
交换两个变量
golang可以直接复制交换,无需设置第三个中间变量
【2】数据类型
整数类型
int 为正负数
unit 为正数
其他类型
Golang程序中整型变量在使用时,遵守保小不保大的原则
即:在保证程序正确运行下,尽量使用占用空间小的数据类型
(如需要设置一个学生的年龄变量,年龄不会存在负数并且大概率不会超过255,所以选择 uint8 或直接用byte类型较为合适)
浮点类型
float32可能会造成精度丢失,一般推荐用float64
【2.1】数据类型 -- 指针
package main
import "fmt"
func main() {
var num int = 10
fmt.Printf("num = %v \n",num)
//1、ptr 是一个指针变量
//2、ptr 的类型是 *int
//3、ptr 的值是 &num ,也就是变量num的地址
var ptr *int = &num
fmt.Printf("ptr = %v \n",*ptr)
*ptr = 55
fmt.Printf("ptr = %v \n num = %v \n",*ptr,num)
num = 9
fmt.Printf("ptr = %v \n num = %v \n",*ptr,num)
}
执行结果为
变量num的值为10 ,它在内存中的地址可以在前面加&符号获取,写作&num。
变量ptr在定义时使用了这样的写法 var ptr *int = &num,就是将num的地址当做值保存了起来。
在使用ptr时,前面加上 * 符号就可以通过保存的地址找到对应的变量num的值。
这一系列的操作类似于php中的传引用。
【2.2】数据类型 -- 自定义数据类型
如下代码可以自定义数据类型,就是复制int32类型取名为myInt类型,类似取别名。
type myInt int32
但是要注意,虽然它复制了int32 ,但并不能和原本的类型互相赋值
【2.3】数据类型 -- 值类型和引用类型
值类型:基本数据类型int系列, float系列, bool, string 、数组和结构体struct
值类型通常储存在栈区中
引用类型:指针、slice切片、map、管道chan、interface等都是引用类型
引用类型通常储存在堆区中
其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。
【2.4】数据类型 -- 数组
数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
从下图可以看出数组在内存中的地址为该数组第一个元素的地址(地址为16进制),并且后续元素的地址为上一个地址+8(+8是因为在我的机器是64位时 int 默认为 int64 类型占8字节,如果是 int32 类型则占4字节就是+4)
声明一个数组,int类型默认值是0 ,string类型默认值是 "" , bool类型默认值是false
package main
import "fmt"
func main() {
var arr1 [3]int
fmt.Printf("arr1的类型是%T\n", arr1) //arr1的类型是[3]int
fmt.Println(arr1) //[0 0 0]
}
给数组赋值
var arr1 [3]int
arr1[0] = 10
arr1[1] = 15
fmt.Printf("arr1的类型是%T\n", arr1) //arr1的类型是[3]int
fmt.Println(arr1) //[10 15 0]
四种初始化数组的方式
var arr1 [3]float64 = [3]float64 {1.1, 2.3, 10.0}
fmt.Printf("arr1 的类型是%T 值是%v\n", arr1, arr1)
//arr1 的类型是[3]float64 值是[1.1 2.3 10]
var arr2 = [3]string {"tom", "jack", "lucy"}
fmt.Printf("arr2 的类型是%T 值是%v\n", arr2, arr2)
//arr2 的类型是[3]string 值是[tom jack lucy]
var arr3 = [...]int {1, 3, 5}//... 系统自动判断数组长度
fmt.Printf("arr3 的类型是%T 值是%v\n", arr3, arr3)
//arr3 的类型是[3]int 值是[1 3 5]
arr4 := [...]int {1:666, 0:555, 2:777}//指定元素值对应的下标
fmt.Printf("arr4 的类型是%T 值是%v\n", arr4, arr4)
//arr4 的类型是[3]int 值是[555 666 777]
数组遍历,使用for - range 。这是go语言一种特有的结构,可以用来遍历数组的元素。具体写法如下。
1、第一个返回值 index 为数组的下标
2、第二个返回值 value 为该下标位置的值
3、如果不想使用下标的话可以用 _ 代替index
4、index 和 value 都是仅在这个for循环内部可以使用的局部变量
5、index 和 value不是固定写法,可以自行命名
package main
import "fmt"
func main() {
arr := [...]int64 {1, 2, 3, 8, 10}
for index, value := range arr {
fmt.Printf("下标为%v的元素值为%v\n", index, value)
}
}
输出结果为:
【2.4.1】数据类型 -- 二维数组
使用以下代码定义了一个二维数组,该数组有两个子数组,每个子数组有三个元素。
然后将第二个子数组的第二个元素赋值了10。
package main
import (
"fmt"
)
func main() {
var arr2 [2][3]int // 声明一个二维数组arr2,包含2行3列
arr2[1][1] = 10 // 给arr2的第2行第2列赋值为10(数组下标从0开始)
fmt.Println(arr2) // 打印arr2数组
}
输出结果为
内存分析:
arr2 有两个地址 可以看出是他的两个子数组的第一个元素的地址
【2.5】数据类型 -- 切片
1、定义切片并引用一个数组。
[1:3] 的意思是 从下标 1 开始到 3 结束,但是引用的元素包括下标 1 不包括下标 3
如果要取到最后一个值则 :后面不写下标 ,示例([1:] 、 [:3] 、 [:])
package main
import "fmt"
func main() {
arr := [...]int{1, 2, 3, 4, 5}
slice := arr[1:3]
//slice的类型是[]int,值是[2 3]
fmt.Printf("slice的类型是%T,值是%v",slice,slice)
}
切片是引用类型,从下面可以看出,切片和数组的同一个元素的地址是相同的
package main
import "fmt"
func main() {
arr := [...]int{1, 2, 3, 4, 5}
slice := arr[1:3]
fmt.Printf("arr[1]的地址是%p\n", &arr[1])
fmt.Printf("slice[0]的地址是%p\n", &slice[0])
slicelen := len(slice)
sliceCap := cap(slice)
fmt.Printf("slice的长度是%v,slice的容量是%v\n", slicelen, sliceCap)
}
输出结果:
修改其中一个的值,另一个也会随之变化
内存示意图:slice实际上只存了arr[1]的地址还有本身的长度和容量。
2、用内置函数make来定义一个切片
基本语法: var 名称 []type = make([]type, len, [cap])
参数说明:type:数据类型 len:切片长度 cap:容量(可选参数)
package main
import "fmt"
func main() {
var slice []float64 = make([]float64, 5, 10)
slice[1] = 99
slice[3] = 100
fmt.Println(slice)
fmt.Println("slice的长度是", len(slice))
fmt.Println("slice的容量是", cap(slice))
}
输出结果
内存示意图:本质上还是引用了一个数组,但是这个数组是不可见的,只有通过该切片可以使用
3、定义一个切片直接指向具体数组,使用原理类似make
package main
import "fmt"
func main() {
var slice []string = []string{"tom","jack","lucy"}
fmt.Println(slice)
fmt.Println("slice的长度是", len(slice))
fmt.Println("slice的容量是", cap(slice))
}
输出结果
【2.6】数据类型 -- map
数据类型格式:map(键类型)值类型
package main
import "fmt"
func main() {
//第一种定义方法 使用make函数
// var valMap map[string]any
valMap := make(map[any]any, 5)
valMap["XX1"] = "x5x5"
valMap[2] = "hello"
valMap[15] = "world"
valMap[8] = "nihao"
valMap[0] = 1
for i, v := range valMap {
fmt.Printf("%v=>%v\n", i, v)
}
fmt.Println("--------------------------------")
//第二种定义方法,定义map时直接赋值
heroes := map[string]string{
"no1": "武松",
"no2": "李逵",
}
fmt.Println(heroes)
fmt.Println("--------------------------------")
//map嵌套
students := make(map[string]map[string]string)
students["stu01"] = make(map[string]string)
students["stu01"]["name"] = "小明"
students["stu01"]["sex"] = "男"
students["stu02"] = make(map[string]string)
students["stu02"]["name"] = "小花"
students["stu02"]["sex"] = "女"
fmt.Println(students)
fmt.Println("--------------------------------")
}
输出结果
根据上面的循环打印输出结果可以看出map内部是无序的
【2.6.1】数据类型 -- map的curd
1、查询
package main
import "fmt"
func main() {
heroes := map[string]string{
"no1": "武松",
"no2": "李逵",
"no3": "宋江",
}
//查询某个key是否存在
no1, ok := heroes["no1"]
if ok {
fmt.Println("ok = ", ok)
fmt.Println("no1 = ", no1)
}
no4, ok := heroes["no4"]
if ok {
fmt.Println(no4)
} else {
fmt.Println("没有no4,ok = ", ok)
}
fmt.Println("-----------------------------")
fmt.Println(heroes)
}
输出结果
2、删除
//删除一个元素 delete(要删除的map原值,要删除的key)
delete(heroes, "no1")
//删除全部元素 for循环删除 或者直接重新make
heroes = make(map[string]string)
3、创建和更新
package main
import "fmt"
func main() {
heroes := map[string]string{
"no1": "武松",
"no2": "李逵",
"no3": "宋江",
}
//查询no1是否存在,如果存在就更新为林冲
_, ok := heroes["no1"]
if ok {
heroes["no1"] = "林冲"
}
//查询no4是否存在,如果不存在就添加
_, ok2 := heroes["no4"]
if !ok2 {
heroes["no4"] = "扈三娘"
}
fmt.Println("-----------------------------")
fmt.Println(heroes)
}
【2.6.2】数据类型 -- map切片
package main
import "fmt"
func main() {
//创建一个map切片 必须使用make函数
mapSlice := make([]map[string]any, 2)
//给切片添加一个map
mapSlice[0] = make(map[string]any)
mapSlice[0]["name"] = "tom"
mapSlice[0]["age"] = 13
//使用append给切片添加两个map
mapSlice2 := map[string]any{
"name": "jack",
"age": 16,
}
mapSlice = append(mapSlice, mapSlice2, map[string]any{
"name": "lucy",
"age": 14,
})
fmt.Println(mapSlice)
fmt.Println("--------------------------------")
fmt.Println(mapSlice[3]["name"])
}
【3】常量
使用const可以定义常量:
1、常量必须给初始值
2、常量必须是编译时就可以确定的值,并且不能被修改
3、可以批量定义常量
4、常量不必全部大写
5、常量仍是通过首字母大小写来控制是否可以被其他包访问
6、iota可以给常量赋值为当前const内的第n行。后面常量的如果没给值会+1
package main
import "fmt"
const TestInt = 10
const TestString string = "hello"
func main() {
const (
a = 9
b = iota
c
d
e = 50
)
fmt.Println(TestInt, TestString)
fmt.Println(a, b, c, d, e)
}