指针/nil/声明变量/new

指针:实际就是创建了一块内存,让它指向了真正存储数据的地址的地址。

  1. var v1 *int

new关键字:

  1. v2 := new(int)

用于创建内存并进行内部数据初始化,并返回一个指针类型

nil关键字

代指go语言里的空置var v100 _intvar v101 _int8默认都是空值,指向的都是nil

超大整型

创建超大整型的对象

  1. package main
  2. import (
  3. "fmt"
  4. "math/big"
  5. )
  6. func main() {
  7. var v1 big.Int
  8. fmt.Println(v1)
  9. var v2 *big.Int
  10. fmt.Println(v2) // 指向空的内存地址,不可以写值 一般用不到,一般在直接给他赋值的时候使用
  11. v4 := new(big.Int)
  12. fmt.Println(v4) // 也是初始化后的数据
  13. // 在大型整型对象里写值
  14. v1.SetInt64(1998)
  15. v1.SetString("9999999999999999", 10) // 把这个字符串以10进制的方式写值
  16. }

基本上就会使用

  1. var v1 big.Int
  2. fmt.Println(v1)

  1. v4 := new(big.Int)
  2. fmt.Println(v4) // 也是初始化后的数据

推荐:使用指针的方式,即使用**new**来进行创建和初始化。


基本的加减乘除

加法:

  1. n1 := new(big.Int)
  2. n1.SetInt64(89)
  3. n2 := new(big.Int)
  4. n2.SetInt64(99)
  5. result := new(big.Int)
  6. result.Add(n1, n2)
  7. fmt.Println(result)

简写

  1. n1 := new(89)
  2. n2 := new(99)
  3. result := new(big.Int)
  4. result.Add(n1, n2)
  5. fmt.Println(result)

其他

  1. // 减
  2. result := new(big.Int)
  3. result.Sub(n1, n2)
  4. // 乘
  5. result.Mul(n1, n2)
  6. // 除(地板处,只能得到商)
  7. result.Div(n1, n2)
  8. // 除,得商和余数
  9. minder := new(big.Int)
  10. result.DivMod(n1, n2, minder)
  11. fmt.Println(result.Int64(), result.String()) // 以这2个格式进行输出

建议

  • 尽量new方式去初始化并返回一个指针类型的方式。
  • 易错的点:int类型和*int类型

浮点型

浮点数,计算机中小数的表示方式,如3.14

Go语言中提供了2种浮点类型:

  • float32,用32位(4个字节)来存储浮点型。
  • float64,用64位(8个字节)来存储浮点型。

非精确

float类型,计算机中小数的非精确的表示方式

  1. package main
  2. import "fmt"
  3. func main() {
  4. v1 := 0.1
  5. v2 := 0.2
  6. result := v1 + v2
  7. fmt.Println(result)
  8. v3 := 0.3
  9. v4 := 0.2
  10. fmt.Println(v3 + v4)
  11. }

输出

  1. 0.30000000000000004
  2. 0.5

float底层存储原理

  1. var price float32 = 39.29

第一步:浮点型转换为二进制

  • 整数部分:直接转换为二进制(10进制转换为2进制),
  • 小数部分:让小数部分乘以2,如果结果小于1则继续将结果乘以2,如果结果大于1,则让结果-1再继续乘以2,直到结果=1结束。

第二步:科学计数法表示

数据类型 - 图1

第三步:存储

以float32为例来进行存储,用32位来存储浮点型。

  • sign,用1位来表示浮点数正负,0表示正数,1表示负数
  • exponent,8位来表示的话,总共有256种(0 ~ 255),含正负值(-127 ~ 128),例如,5需要存储到exponent位的话,需要让5+127=132,再将132转换二进制,存储到exponent。
  • fraction,存小数点后的所有的数据。如果超出这个范围,就不要了,所以它不精确。

decimal

用于表示精确的小数

Go语言内部没有decimal

第三方包,则需要在本地的go环境中先安装再使用,第三方源码包地址:https://github.com/shopspring/decimal

第一步:安装第三方包

  1. go get github.com/shopspring/decimal

命令执行完之后吗,会出现在$GOPATH/src的目录下出现github.com/shopspring/decimal的目录,这就是第三方模块安装的位置。

第二步:使用decimal包

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/shopspring/decimal"
  5. )
  6. func main() {
  7. var v1 = decimal.NewFromFloat(0.0000000019)
  8. var v2 = decimal.NewFromFloat(0.01)
  9. var v3 = v1.Add(v2)
  10. fmt.Println(v3)
  11. }

字符串

本质:所有的数据和操作最终都是二进制

Go语言的字符串是utf-8编码的序列。

  1. var name string = "wujie" // 内部会进行utf-8编码
  1. username := "无解"
  2. // 获取字符串的长度,字节长度
  3. fmt.Println(len(username))
  4. // 字符串转换为一个字节集合
  5. byteStr := []byte(username)
  6. // [230 151 160 232 167 163] 以10进制的方式进行展示
  7. fmt.Println(byteStr)
  8. // 字节的集合转换为字符串
  9. byteList := []byte{230, 151, 160, 232, 167, 163}
  10. targetString := string(byteList)
  11. fmt.Println(targetString)
  1. // 结果
  2. 6
  3. [230 151 160 232 167 163]
  4. 无解
  1. username := "无解"
  2. // 获取字符串的长度,字节长度
  3. fmt.Println(len(username))
  4. // 字符串转换为一个字节"集合"
  5. byteStr := []byte(username)
  6. // [230 151 160 232 167 163] 以10进制的方式进行展示
  7. fmt.Println(byteStr)
  8. // 字节的"集合"转换为字符串
  9. byteList := []byte{230, 151, 160, 232, 167, 163}
  10. targetString := string(byteList)
  11. fmt.Println(targetString)
  12. // rune的"集合"
  13. // 将一个字符串转换为unicode字符集码点的"集合"
  14. tempSet := []rune(name)
  15. fmt.Println(tempSet) // [26080 20329 22855]
  16. fmt.Println(tempSet[0], strconv.FormatInt(int64(tempSet[0]), 16))
  17. fmt.Println(tempSet[1], strconv.FormatInt(int64(tempSet[1]), 16))
  18. fmt.Println(tempSet[2], strconv.FormatInt(int64(tempSet[2]), 16))
  19. // 长度的处理
  20. runeLength := utf8.RuneCountInString(username)
  21. fmt.Println(runeLength) // 2

Go语言没有集合概念,这里加了引号,只是为了方便理解

常用功能

获取字符串的长度

  1. username := "无解"
  2. // 获取字符串的长度,字节长度
  3. fmt.Println(len(username))
  4. // 长度的处理 获取字符长度
  5. runeLength := utf8.RuneCountInString(username)
  6. fmt.Println(runeLength) // 2

是否是以XX开头

  1. username := "无解"
  2. // 判断字符串是否是以无开头
  3. result := strings.HasPrefix(username, "无")
  4. fmt.Println(result) // 输出true

是否以xx结尾

  1. username := "无解"
  2. // 判断字符串是否是以解 结尾
  3. result := strings.HasSufix(username, "解")
  4. fmt.Println(result) // 输出true

是否包含

  1. username := "无解"
  2. // 判断字符串是否包含一些指定的文本
  3. result := strings.Contains(username, "解")
  4. fmt.Println(result) // 输出true

变大写和变小写

  1. username := "wujie"
  2. result := strings.ToUpper(username)
  3. fmt.Println(result) // 输出 WUJIE

注意:result是大写的,但是username依然是小写的

  1. username := "WUJIE"
  2. result := strings.ToLower(username)
  3. fmt.Println(result) // 输出 wujie

注意点同上!

去两边

  1. username := "wujie"
  2. result1 := strings.TrimRight(username, "e") // 去除右边的e
  3. result2 := strings.TrimLeft(username, "w") // 去除左边的w
  4. result3 := strings.Trim(username, "w") // 去除两边的w
  5. fmt.Println(result1, result2, result3) // 输出 wuji ujie ujie

注意点同上,去除后的结果会变,但是原变量不会变!

替换

  1. name := "wujie"
  2. result1 := strings.Replace(name, "j", "PE", 1) // 找到j替换为PE,从左边到右边第一个
  3. result2 := strings.Replace(name, "j", "PE", 2) // 找到j替换为PE, 从左到右找前两个替换
  4. result3 := strings.Replace(name, "j", "PE", -1) // 找到j替换为PE, 替换所有
  5. fmt.Println(result1, result2, result3)

分割

  1. name := "无解的大炮"
  2. result := strings.Split(name, "的")
  3. // 根据 的 进行切割,获取一个切片,类似一个数组
  4. fmt.Println(result) // [无解, "大炮"]

拼接

  1. message := "我爱" + "北京天安门" // 不建议,效率低
  2. fmt.Println(message)
  3. // 建议:效率高一点
  4. dataList := []string{"我爱", "北京天安门"}
  5. result := strings.Join(dataList, "-") // 第二个参数为连接符,不写则没有
  6. fmt.Println(result) // 输出 我爱-北京天安门
  7. // 效率更高一点 go1.10之前
  8. var buffer bytes.Buffer
  9. buffer.WriteString("你想")
  10. buffer.WriteString("我想")
  11. buffer.WriteString("她")
  12. data := buffer.String()
  13. fmt.Println(data)
  14. // 建议:效率更加高一点 go1.10之后
  15. var builder strings.Builder
  16. builder.WriteString("哈哈哈哈")
  17. builder.WriteString("去你的吧")
  18. value := builder.String()
  19. fmt.Println(value)

string和int互转

  1. num := "666"
  2. var data, _ = strconv.Atoi(num)
  3. fmt.Println(data)
  4. // 整型转字符串 可用于处理进制转换
  5. var result, err = strconv.ParseInt(num, 10, 32)
  6. fmt.Println(result, err)
  1. var result = strconv.Itoa(888)
  2. fmt.Println(result) // 将整型转换为字符串

string和字符

  1. v1 := string(65)
  2. fmt.Println(v1) // A
  3. // 字符串转数组
  4. v2, size := utf8.DecodeRuneInString("A")
  5. fmt.Println(v2, size) // 65 size: 最短可表示的字节 这里输出 1

索引切片和循环

  1. package main
  2. import "fmt"
  3. func main() {
  4. var name string = "wujie"
  5. // 1. 索引获取字节
  6. fmt.Println(name[0]) // 119
  7. // 2. 切片获取字节空间
  8. fmt.Println(name[2:3]) // j
  9. // 3. 手动循环获取所有字节
  10. for i := 0; i < len(name); i++ {
  11. fmt.Println(i, name[i])
  12. }
  13. /*
  14. 0 119
  15. 1 117
  16. 2 106
  17. 3 105
  18. 4 101
  19. */
  20. // 4. for range循环获取所有字符
  21. for index, item := range name {
  22. fmt.Println(index, item, string(item))
  23. }
  24. /**
  25. 1 117
  26. 2 106
  27. 3 105
  28. 4 101
  29. */
  30. // 5. 转换成rune集合
  31. dataList := []rune(name)
  32. fmt.Println(dataList[0], string(dataList[0])) // 119 w
  33. }

数组

数组、定长切元素类型一致的数据集合。

方式1:先声明再赋值,声明时,内存已开辟空间

  1. var numbers [3]int // 声明了一个数组,长度是3,元素类型都是int
  2. // 进行赋值
  3. numbers[0] = 999
  4. numbers[1] = 666
  5. numbers[2] = 333

方式2:声明+赋值

  1. var names = [2]string{"hello", "world"}

方式3:声明+赋值+指定位置

  1. var ages = [3]int{0:87, 1:73, 2:99} // 在第0个位置赋值87,第1个位置赋值73,第2个位置赋值99

方式4:省略个数

  1. var names = [...]string{"hello", "world"} // ... 会自动根据你的值获取个数,长度=2
  1. // 声明:指针类型的数组,不会开辟内存初始化数组中的值,numbers = nil
  2. var numbers *[3]int
  3. // 声明数组并初始化,内部都是0,唯一的差别,返回的是一个指针类型的数据
  4. numbers := new([3]int)

数组的内存管理

  • 数组的内存是连续的
  • 数组的内存地址实际上就是数组第一个元素的内存地址
  • 每个字符串的内部存储的格式:
    • len+str分别占8个字节
      1. type stringStruct struct {
      2. str unsafe.Pointer
      3. len int
      4. }

      示例1
  1. package main
  2. import "fmt"
  3. func main() {
  4. nums := [3]int8{11, 22, 33}
  5. fmt.Printf("数组的内存地址%p\n", &nums)
  6. fmt.Printf("数组的第1个元素的地址%p\n", &nums[0])
  7. fmt.Printf("数组的第2个元素的地址%p\n", &nums[1])
  8. }
  9. >>> 输出
  10. 数组的内存地址0x1400001a092
  11. 数组的第1个元素的地址0x1400001a092
  12. 数组的第2个元素的地址0x1400001a093

示例2

  1. package main
  2. import "fmt"
  3. func main() {
  4. nums := [3]int32{11, 22, 33}
  5. fmt.Printf("数组的内存地址%p\n", &nums)
  6. fmt.Printf("数组的第1个元素的地址%p\n", &nums[0])
  7. fmt.Printf("数组的第2个元素的地址%p\n", &nums[1])
  8. }
  9. >>>输出
  10. 数组的内存地址0x140000a6004
  11. 数组的第1个元素的地址0x140000a6004
  12. 数组的第2个元素的地址0x140000a6008

示例3

  1. names := [2]string{"wujie", "virus"}
  2. fmt.Printf("内存地址%p\n", &names)
  3. fmt.Printf("内存地址%p\n", &names[0])
  4. fmt.Printf("内存地址%p\n", &names[1])
  5. >>>输出
  6. 内存地址0x1400012a000
  7. 内存地址0x1400012a000
  8. 内存地址0x1400012a010

数组的可变和拷贝

数组的两个特性和特点:可变、拷贝

可变:数组的元素可以被更改

长度和类型都不可以修改

  1. names := [2]string{"wujie", "virus"}
  2. names[1] = "lala"

注意:字符串不可以被修改!


拷贝,重新拷贝,变量赋值时会重新拷贝一份。

  1. name1 := [2]string{"wujie", "virus"}
  2. name2 := name1 // 重新拷贝一份
  3. name1[1] = "lala" // 修改其中一个,另外一个是不会发生变化的
  4. fmt.Println(name1, name2)
  5. // name1: [wujie lala]
  6. // name2: [wujie virus]

数组的切片和循环

  1. nums := [3]int32{11, 22, 33}
  2. // 数组的切片
  3. data := nums[0:2] // 获取下标大于等于0小于2
  4. fmt.Println(data)
  5. // 数组的循环
  6. for i := 0; i < len(nums); i++ {
  7. fmt.Println(nums[i])
  8. }
  9. // for range 循环
  10. for key, item := range nums { // 如果只写一个,获取的只能是索引
  11. fmt.Println(key, item)
  12. }
  13. for _, item := range nums {
  14. fmt.Println(item)
  15. }