golang学习笔记(数据类型)

整型
在这里插入图片描述
int8和uint8,8代表8个bit,能代表的数值个数有2^8 = 256
uint8没符号,额能代表的是正数0-258
int8可正可负,区间-128~127

int8、int16、int32、int64带数值,表示的数值个数是固定的,
int没有指定位数,他是可以变化的
当你在32位系统下,int和uint都占用4个字节,也就是32位,当你在64位系统下,int和uint都占8个字节,也就是64位

在二进制传输、读写文件的结构,为了保存文件的结构不受不同的编译目标平台字节长度影响,避免使用int和uint,而是使用更加精确的int32和int64

不同进制的表示方法

package main

import "fmt"

func main(){
	var num01 int = 0b1100
	var num02 int = 0o14
	var num03 int = 0xC
	fmt.Printf("2进制数 %b 表示的是: %d \n", num01, num01)
    fmt.Printf("8进制数 %o 表示的是: %d \n", num02, num02)
    fmt.Printf("16进制数 %X 表示的是: %d \n", num03, num03)
}

输出
2进制数 1100 表示的是: 12
8进制数 14 表示的是: 12
16进制数 C 表示的是: 12

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

浮点型
浮点数类型的值由整数部分、小数点"."和小数部分组成
整数部分和小数部分由10进制表示法表示。
另外一种表示方法,就是在其中加入指数。指数部分由"E"或"e"以及一个带正负号的十进制数组成。
比如3.7E-2表示浮点数0.037,3.7E+1表示浮点数37

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

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

那么精度是什么意思?有效位有多少位?

精度主要取决于尾数部分的位数。

对于 float32(单精度)来说,表示尾数的为23位,除去全部为0的情况以外,最小为2-23,约等于1.19*10-7,所以float小数部分只能精确到后面6位,加上小数点前的一位,即有效数字为7位。

同理 float64(单精度)的尾数部分为 52位,最小为2-52,约为2.22*10-16,所以精确到小数点后15位,加上小数点前的一位,有效位数为16位。

总结

1、float32和float64可以表示的数值很多
浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 math 包中找到:

常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38;

常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308;

float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。

2、 数值很大但精度有限
float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度

float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度

精度是什么?
比如10000018这个数,用float32的类型来表示的话,由于有效位是7位,科学计数法就是1.0000018 * 10^7,精确到小数点后面7位

package main
import "fmt"
func main(){
	var num1 float32 = 10000018
	var num2 float32 = 100000182
	var num3 float32 = 100000187
	fmt.Println("num1 :",num)
	fmt.Println("num1+1 :",num+1)
	fmt.Println("num2 :",num2)
	fmt.Println("num2+5 :",num+5)
	fmt.Println(num1 == num2+5)
}
输出
num1:  1.0000018e+07
num1+1:  1.0000019e+07
num2:  1.00000184e+08
num2+5:  1.0000019e+08
false

byte、rune与字符串
byte占一个字节,就是8bit表示范围0~255,和uint8类型本质上没有区别,表示的是ACSII表中的一个字符

import "fmt"

func main() {
    var a byte = 65
    // 8进制写法: var a byte = '\101'     其中 \ 是固定前缀
    // 16进制写法: var a byte = '\x41'    其中 \x 是固定前缀

    var b uint8 = 66
    fmt.Printf("a 的值: %c \nb 的值: %c", a, b)

    // 或者使用 string 函数
    // fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b))
}
输出
a 的值: A
b 的值: B

rune占4个字节,32bit,跟int32本质上没区别。表示的是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 个字节数

上面的例子都是使用单引号,在GO中单引号不等价与双引号
单引号是用来表示字符
双引号是用来表示字符串

byte和uint8、rune和uint32有没有区别?
因为uint8和uint32,直观上让人以为是一个数值,但实际上,也可以表示一个字符,所以为了消除直观错觉,就有了byte和rune

字符串

var str string = "hello world!"

byte和rune都是字符类型,多个字符放一起,就组成了字符串string类型

import (
    "fmt"
)

func main() {
    var mystr01 string = "hello"
    var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111}
    fmt.Printf("mystr01: %s\n", mystr01)
    fmt.Printf("mystr02: %s", mystr02)
}
输出
mystr01: hello
mystr02: hello

占用字节题
”hello 世界“占了多少个字节?
Go语言的string是用utf-8编码,英文字母占一个字节,汉字占三个字节,空格、逗号这些也是占一个字节,所以总共占用5+1+(3*2)个字节

数组与切片

数组是固定长度的特定类型元素组成的序列,因为长度固定,所以很少使用

//声明一个长度为3的数组
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3

//声明并直接初始化
var arr [3]int = [3]int{1,2,3}
arr := [3]int{1,2,3}

//...让Go自己根据实际情况来分配空间
arr := [...]int{1,2,3}

//偷懒定义数组
arr := [4]int{2:3} //[0,0,3,0]

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

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

1、对数组进行片段截取

myarr := [5]int{1,2,3,4,5}
// 【第一种】
// 1 表示从索引1开始,直到到索引为 2 (3-1)的元素
mysli1 := myarr[1:3]

// 【第二种】
// 1 表示从索引1开始,直到到索引为 2 (3-1)的元素
mysli2 := myarr[1:3:4]

打印出来他们是一样的,第二种的4是终止索引
在切片的时候,若不指定第三个数,那么切片终止索引会一直到原数组的最后一个数。如果指定第三个数,那么切片终止索引只会到原数组的该索引值,切片的第三个数影响的只是切片的容量,而不会影响长度

package main

import "fmt"

func main(){
    myarr := [5]int{1,2,3,4,5}
    fmt.Printf("myarr 的长度为:%d,容量为:%d\n", len(myarr), cap(myarr))

    mysli1 := myarr[1:3]
    fmt.Printf("mysli1 的长度为:%d,容量为:%d\n", len(mysli1), cap(mysli1))
    fmt.Println(mysli1)

    mysli2 := myarr[1:3:4]
    fmt.Printf("mysli2 的长度为:%d,容量为:%d\n", len(mysli2), cap(mysli2))
    fmt.Println(mysli2)
}
输出
myarr 的长度为:5,容量为:5
mysli1 的长度为:2,容量为:4
[2 3]
mysli2 的长度为:2,容量为:3
[2 3]

2、从头声明赋值

//声明字符串切片
var strList []string
//声明整形切片
var numList []int
//声明一个空切片
var emptyList []int{}

3、使用make构造

make([]Type, size, cap)

import "fmt"

func main(){
	a := make([]int,2)
	b := make([]int,2,10)
	fmt.Println(a, b)
    fmt.Println(len(a), len(b))
    fmt.Println(cap(a), cap(b))
}
输出
[0 0] [0 0]
2 2
2 10

4、偷懒方式

import (
 "fmt"
)

func main() {
    a := []int{4:2}
    fmt.Println(a)
    fmt.Println(len(a), cap(a))
}
输出
[0 0 0 0 2]
5 5

切片是引用类型,不对它赋值,它的零值(默认值)是nil

切片可以随时增加长度

import (
    "fmt"
)

func main() {
    myarr := []int{1}
    // 追加一个元素
    myarr = append(myarr, 2)
    // 追加多个元素
    myarr = append(myarr, 3, 4)
    // 追加一个切片, ... 表示解包,不能省略
    myarr = append(myarr, []int{7, 8}...)
    // 在第一个位置插入元素
    myarr = append([]int{0}, myarr...)
    // 在中间插入一个切片(两个元素)
    myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...)
    fmt.Println(myarr)
}
[0 1 2 3 4 5 6 7 8]

Map类型

map类型是由若干个key:value这样的键值对映射组合在一起的数据结构
他是哈希表的一个实现,也就是要求它的每个映射里的key都是唯一的,可以用==和!=来进行判等操作,也就是锁key必须是可哈希的

什么是哈希?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、map,函数之外的其他内建类型都算)

map[KEY_TYPE]VALUE_TYPE
声明初始化map

//第一种方法
var score map[string]int = map[string]int{"a":1,"b":2}

//第二种
score := map[string]int = map[string]int{"a":1,"b":2}

//第三种
score := make(map[string]int)
score["a"] = 1
score["b"] = 2

常用第三种,第一种未初始化make(map[string]int)零值为nil,无法进行赋值

//添加元素,若key已存在,直接更新value
score["a"] = 1
//读取元素
fmt.Println(score["a"])
//删除元素是
delete(socre["a"])

//循环读取
for key, value := range score{
	fmt.Printf("key: %s, value: %d\n", key, value)
}
for key := range score{
	fmt.Printf("key: %s\n", key)
}
for _, value := range score{
	fmt.Printf("value: %d\n", value)
}

布尔类型

在Go中,真值用true,不与1相等,假值用false不与0相等

指针

var name string = "王小明"

当我们访问这个变量时,计算机会返回给我们它指向的内存地址里面存储的值:王小明
将这个内存地址赋值给另外一个变量名,叫做pointer,而这个变量,我们称之为指针变量
普通变量:存数据本身
指针变量:存值的内存地址

创建指针

//方法一
aint := 1
ptr := &aint

//方法二
//创建指针
astr := new(string)
//指针赋值
*astr = "hello world"

//第三种
aint := 1
var bint *int//声明一个指针
bint = &aint//初始化

&:从一个普通变量中取得内存地址
星号*:当星号*在赋值操作符(=)的右边,是从一个指针变量总取得变量值,当在左边是指该指针指向的变量

package main

import "fmt"

func main() {
    aint := 1     // 定义普通变量
    ptr := &aint  // 定义指针变量
    fmt.Println("普通变量存储的是:", aint)
    fmt.Println("普通变量存储的是:", *ptr)
    fmt.Println("指针变量存储的是:", &aint)
    fmt.Println("指针变量存储的是:", ptr)
}
输出
普通变量存储的是: 1
普通变量存储的是: 1
指针变量存储的是: 0xc0000100a0
指针变量存储的是: 0xc0000100a0

指针类型

package main

import "fmt"

func main() {
    astr := "hello"
    aint := 1
    abool := false
    arune := 'a'
    afloat := 1.2

    fmt.Printf("astr 指针类型是:%T\n", &astr)
    fmt.Printf("aint 指针类型是:%T\n", &aint)
    fmt.Printf("abool 指针类型是:%T\n", &abool)
    fmt.Printf("arune 指针类型是:%T\n", &arune)
    fmt.Printf("afloat 指针类型是:%T\n", &afloat)
}
输出
astr 指针类型是:*string
aint 指针类型是:*int
abool 指针类型是:*bool
arune 指针类型是:*int32
afloat 指针类型是:*float64

若我们定义一个只接收指针类型的参数的函数,可以这么写

func mytest(ptr *int){
	fmt.Println(*ptr)
}

指针零值

//当指针声明后,没有进行初始化,其零值是nil
func main(){
	a := 25
	var b *int
	if b == nil{
		fmt.Println(b)
		b = &a//初始化,将a的内存地址给b
		fmt.Println(b)
	}
}
输出
<nil>
0xc0000100a0

切片与指针一样都是引用类型

如果我们想通过一个函数改变一个数组的值,有两种方法
1、将这个数组的切片作为参数传递给函数
2、将这个数组的指针作为参数传递给函数

使用切片

func main(){
	arr := [3]int{1,2,3}
	modify(arr[:])
	fmt.Println(arr)
}
func modify(arr []int){
	arr[1] = 4
}

使用指针

func main(){
	arr := [3]int{1,2,3}
	modify(&arr)
	fmt.Println(arr)
}
func modify(arr *[3]int){
	(*arr)[1] = 4
}

尽管两者都可以实现我们的目的,但是按照Go语言的使用习惯,建议用第一种,因为写出来代码更加简洁易读

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值