指针/nil/声明变量/new
指针:实际就是创建了一块内存,让它指向了真正存储数据的地址的地址。
var v1 *int
new关键字:
v2 := new(int)
用于创建内存并进行内部数据初始化,并返回一个指针类型
nil关键字
代指go语言里的空置var v100 _intvar v101 _int8默认都是空值,指向的都是nil
超大整型
创建超大整型的对象
package main
import (
"fmt"
"math/big"
)
func main() {
var v1 big.Int
fmt.Println(v1)
var v2 *big.Int
fmt.Println(v2) // 指向空的内存地址,不可以写值 一般用不到,一般在直接给他赋值的时候使用
v4 := new(big.Int)
fmt.Println(v4) // 也是初始化后的数据
// 在大型整型对象里写值
v1.SetInt64(1998)
v1.SetString("9999999999999999", 10) // 把这个字符串以10进制的方式写值
}
基本上就会使用
var v1 big.Int
fmt.Println(v1)
和
v4 := new(big.Int)
fmt.Println(v4) // 也是初始化后的数据
推荐:使用指针的方式,即使用**new**
来进行创建和初始化。
基本的加减乘除
加法:
n1 := new(big.Int)
n1.SetInt64(89)
n2 := new(big.Int)
n2.SetInt64(99)
result := new(big.Int)
result.Add(n1, n2)
fmt.Println(result)
简写
n1 := new(89)
n2 := new(99)
result := new(big.Int)
result.Add(n1, n2)
fmt.Println(result)
其他
// 减
result := new(big.Int)
result.Sub(n1, n2)
// 乘
result.Mul(n1, n2)
// 除(地板处,只能得到商)
result.Div(n1, n2)
// 除,得商和余数
minder := new(big.Int)
result.DivMod(n1, n2, minder)
fmt.Println(result.Int64(), result.String()) // 以这2个格式进行输出
建议
- 尽量new方式去初始化并返回一个指针类型的方式。
- 易错的点:int类型和*int类型
浮点型
浮点数,计算机中小数的表示方式,如3.14
Go语言中提供了2种浮点类型:
- float32,用32位(4个字节)来存储浮点型。
- float64,用64位(8个字节)来存储浮点型。
非精确
float类型,计算机中小数的非精确的表示方式
package main
import "fmt"
func main() {
v1 := 0.1
v2 := 0.2
result := v1 + v2
fmt.Println(result)
v3 := 0.3
v4 := 0.2
fmt.Println(v3 + v4)
}
输出
0.30000000000000004
0.5
float底层存储原理
var price float32 = 39.29
第一步:浮点型转换为二进制
- 整数部分:直接转换为二进制(10进制转换为2进制),
- 小数部分:让小数部分乘以2,如果结果小于1则继续将结果乘以2,如果结果大于1,则让结果-1再继续乘以2,直到结果=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
第一步:安装第三方包
go get github.com/shopspring/decimal
命令执行完之后吗,会出现在$GOPATH/src
的目录下出现github.com/shopspring/decimal
的目录,这就是第三方模块安装的位置。
第二步:使用decimal包
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
var v1 = decimal.NewFromFloat(0.0000000019)
var v2 = decimal.NewFromFloat(0.01)
var v3 = v1.Add(v2)
fmt.Println(v3)
}
字符串
本质:所有的数据和操作最终都是二进制
Go语言的字符串是utf-8
编码的序列。
var name string = "wujie" // 内部会进行utf-8编码
username := "无解"
// 获取字符串的长度,字节长度
fmt.Println(len(username))
// 字符串转换为一个字节集合
byteStr := []byte(username)
// [230 151 160 232 167 163] 以10进制的方式进行展示
fmt.Println(byteStr)
// 字节的集合转换为字符串
byteList := []byte{230, 151, 160, 232, 167, 163}
targetString := string(byteList)
fmt.Println(targetString)
// 结果
6
[230 151 160 232 167 163]
无解
username := "无解"
// 获取字符串的长度,字节长度
fmt.Println(len(username))
// 字符串转换为一个字节"集合"
byteStr := []byte(username)
// [230 151 160 232 167 163] 以10进制的方式进行展示
fmt.Println(byteStr)
// 字节的"集合"转换为字符串
byteList := []byte{230, 151, 160, 232, 167, 163}
targetString := string(byteList)
fmt.Println(targetString)
// rune的"集合"
// 将一个字符串转换为unicode字符集码点的"集合"
tempSet := []rune(name)
fmt.Println(tempSet) // [26080 20329 22855]
fmt.Println(tempSet[0], strconv.FormatInt(int64(tempSet[0]), 16))
fmt.Println(tempSet[1], strconv.FormatInt(int64(tempSet[1]), 16))
fmt.Println(tempSet[2], strconv.FormatInt(int64(tempSet[2]), 16))
// 长度的处理
runeLength := utf8.RuneCountInString(username)
fmt.Println(runeLength) // 2
Go语言没有集合概念,这里加了引号,只是为了方便理解
常用功能
获取字符串的长度
username := "无解"
// 获取字符串的长度,字节长度
fmt.Println(len(username))
// 长度的处理 获取字符长度
runeLength := utf8.RuneCountInString(username)
fmt.Println(runeLength) // 2
是否是以XX开头
username := "无解"
// 判断字符串是否是以无开头
result := strings.HasPrefix(username, "无")
fmt.Println(result) // 输出true
是否以xx结尾
username := "无解"
// 判断字符串是否是以解 结尾
result := strings.HasSufix(username, "解")
fmt.Println(result) // 输出true
是否包含
username := "无解"
// 判断字符串是否包含一些指定的文本
result := strings.Contains(username, "解")
fmt.Println(result) // 输出true
变大写和变小写
username := "wujie"
result := strings.ToUpper(username)
fmt.Println(result) // 输出 WUJIE
注意:result是大写的,但是username依然是小写的
username := "WUJIE"
result := strings.ToLower(username)
fmt.Println(result) // 输出 wujie
注意点同上!
去两边
username := "wujie"
result1 := strings.TrimRight(username, "e") // 去除右边的e
result2 := strings.TrimLeft(username, "w") // 去除左边的w
result3 := strings.Trim(username, "w") // 去除两边的w
fmt.Println(result1, result2, result3) // 输出 wuji ujie ujie
注意点同上,去除后的结果会变,但是原变量不会变!
替换
name := "wujie"
result1 := strings.Replace(name, "j", "PE", 1) // 找到j替换为PE,从左边到右边第一个
result2 := strings.Replace(name, "j", "PE", 2) // 找到j替换为PE, 从左到右找前两个替换
result3 := strings.Replace(name, "j", "PE", -1) // 找到j替换为PE, 替换所有
fmt.Println(result1, result2, result3)
分割
name := "无解的大炮"
result := strings.Split(name, "的")
// 根据 的 进行切割,获取一个切片,类似一个数组
fmt.Println(result) // [无解, "大炮"]
拼接
message := "我爱" + "北京天安门" // 不建议,效率低
fmt.Println(message)
// 建议:效率高一点
dataList := []string{"我爱", "北京天安门"}
result := strings.Join(dataList, "-") // 第二个参数为连接符,不写则没有
fmt.Println(result) // 输出 我爱-北京天安门
// 效率更高一点 go1.10之前
var buffer bytes.Buffer
buffer.WriteString("你想")
buffer.WriteString("我想")
buffer.WriteString("她")
data := buffer.String()
fmt.Println(data)
// 建议:效率更加高一点 go1.10之后
var builder strings.Builder
builder.WriteString("哈哈哈哈")
builder.WriteString("去你的吧")
value := builder.String()
fmt.Println(value)
string和int互转
num := "666"
var data, _ = strconv.Atoi(num)
fmt.Println(data)
// 整型转字符串 可用于处理进制转换
var result, err = strconv.ParseInt(num, 10, 32)
fmt.Println(result, err)
var result = strconv.Itoa(888)
fmt.Println(result) // 将整型转换为字符串
string和字符
v1 := string(65)
fmt.Println(v1) // A
// 字符串转数组
v2, size := utf8.DecodeRuneInString("A")
fmt.Println(v2, size) // 65 size: 最短可表示的字节 这里输出 1
索引切片和循环
package main
import "fmt"
func main() {
var name string = "wujie"
// 1. 索引获取字节
fmt.Println(name[0]) // 119
// 2. 切片获取字节空间
fmt.Println(name[2:3]) // j
// 3. 手动循环获取所有字节
for i := 0; i < len(name); i++ {
fmt.Println(i, name[i])
}
/*
0 119
1 117
2 106
3 105
4 101
*/
// 4. for range循环获取所有字符
for index, item := range name {
fmt.Println(index, item, string(item))
}
/**
1 117
2 106
3 105
4 101
*/
// 5. 转换成rune集合
dataList := []rune(name)
fmt.Println(dataList[0], string(dataList[0])) // 119 w
}
数组
数组、定长切元素类型一致的数据集合。
方式1:先声明再赋值,声明时,内存已开辟空间
var numbers [3]int // 声明了一个数组,长度是3,元素类型都是int
// 进行赋值
numbers[0] = 999
numbers[1] = 666
numbers[2] = 333
方式2:声明+赋值
var names = [2]string{"hello", "world"}
方式3:声明+赋值+指定位置
var ages = [3]int{0:87, 1:73, 2:99} // 在第0个位置赋值87,第1个位置赋值73,第2个位置赋值99
方式4:省略个数
var names = [...]string{"hello", "world"} // ... 会自动根据你的值获取个数,长度=2
// 声明:指针类型的数组,不会开辟内存初始化数组中的值,numbers = nil
var numbers *[3]int
// 声明数组并初始化,内部都是0,唯一的差别,返回的是一个指针类型的数据
numbers := new([3]int)
数组的内存管理
- 数组的内存是连续的
- 数组的内存地址实际上就是数组第一个元素的内存地址
- 每个字符串的内部存储的格式:
len
+str
分别占8个字节type stringStruct struct {
str unsafe.Pointer
len int
}
示例1
package main
import "fmt"
func main() {
nums := [3]int8{11, 22, 33}
fmt.Printf("数组的内存地址%p\n", &nums)
fmt.Printf("数组的第1个元素的地址%p\n", &nums[0])
fmt.Printf("数组的第2个元素的地址%p\n", &nums[1])
}
>>> 输出
数组的内存地址0x1400001a092
数组的第1个元素的地址0x1400001a092
数组的第2个元素的地址0x1400001a093
示例2
package main
import "fmt"
func main() {
nums := [3]int32{11, 22, 33}
fmt.Printf("数组的内存地址%p\n", &nums)
fmt.Printf("数组的第1个元素的地址%p\n", &nums[0])
fmt.Printf("数组的第2个元素的地址%p\n", &nums[1])
}
>>>输出
数组的内存地址0x140000a6004
数组的第1个元素的地址0x140000a6004
数组的第2个元素的地址0x140000a6008
示例3
names := [2]string{"wujie", "virus"}
fmt.Printf("内存地址%p\n", &names)
fmt.Printf("内存地址%p\n", &names[0])
fmt.Printf("内存地址%p\n", &names[1])
>>>输出
内存地址0x1400012a000
内存地址0x1400012a000
内存地址0x1400012a010
数组的可变和拷贝
数组的两个特性和特点:可变、拷贝
可变:数组的元素可以被更改
长度和类型都不可以修改
names := [2]string{"wujie", "virus"}
names[1] = "lala"
注意:字符串不可以被修改!
拷贝,重新拷贝,变量赋值时会重新拷贝一份。
name1 := [2]string{"wujie", "virus"}
name2 := name1 // 重新拷贝一份
name1[1] = "lala" // 修改其中一个,另外一个是不会发生变化的
fmt.Println(name1, name2)
// name1: [wujie lala]
// name2: [wujie virus]
数组的切片和循环
nums := [3]int32{11, 22, 33}
// 数组的切片
data := nums[0:2] // 获取下标大于等于0小于2
fmt.Println(data)
// 数组的循环
for i := 0; i < len(nums); i++ {
fmt.Println(nums[i])
}
// for range 循环
for key, item := range nums { // 如果只写一个,获取的只能是索引
fmt.Println(key, item)
}
for _, item := range nums {
fmt.Println(item)
}