数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
值类型与引用类型
概述
程序中所用到的内存在计算机中使用一堆箱子来表示(这也是人们在讲解它的时候的画法),这些箱子被称为 “字”。根据不同的处理器以及操作系统类型,所有的字都具有 32 位(4 字节)或 64 位(8 字节)的相同长度;所有的字都使用相关的内存地址来进行表示(以十六进制数表示)。
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
另外,像数组和结构体这些复合类型也是值类型。
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
你可以通过&i来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。
值类型的变量的值存储在栈中。
内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。
因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
这个内存地址被称之为指针(你可以从上图中很清晰地看到),这个指针实际上也被存在另外的某一个字中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
在 Go 语言中,指针属于引用类型,其它的引用类型还包括 slices(切片),maps(映射)和 channel(通道)。
被引用的变量会存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间。
值类型
值类型是指变量直接存储了实际的数据,并且每个变量都拥有独立的存储空间。
当一个值类型的变量A被赋给另一个变量B时,会进行值拷贝,对变量B的修改不会影响到原始变量A。
相当于js和python的原始数据类型,赋值后的新变量与原始变量隔离。
常见的值类型
包括所有数字类型(整型、浮点型、复数类型)、字符串类型、布尔型、数组、结构体
//值类型
//所有数字类型
var age int=18
//字符串类型
var str1 string ="hello"
//布尔类型
var isOk bool = true
//数组类型
var arr [5]int =[5]int{1,2,3,4,5}
//结构体类型
type Person struct{
name string;
}
数组类型被称之为array,但是golang中并没有显式使用array这个类型名。
值类型特点
1.直接存储值,不存储地址。
2.变量间赋值或作为函数参数传递时进行值复制。
3.值类型的变量副本是独立的,修改一个变量的副本不会影响另一个。
4.值类型的复制会涉及整个值的拷贝,因此对于大的结构体或数组,复制操作可能会较慢。
5.值类型通常在栈上分配,除非是通过 new 函数分配的,或者是作为闭包中的变量被分配到堆上。
引用类型
引用类型并不直接存储数据本身,而是存储指向数据的指针,当复制一个引用类型的变量时,复制的是指针,新旧变量将指向相同的底层数据。
常见的引用类型
【切片(Slices)】
切片是对数组的封装,提供了一个灵活、动态的视图。当修改切片中的元素时,实际上是在修改底层数组的相应元素。
切片触发底层扩容时会改变地址,其他时候共用之前的地址。
【映射(Maps)】
映射是一种存储键值对的集合。
将映射传递给一个函数或者赋值给另一个变量时,任何对映射的修改都会反映在所有引用了这个映射的地方。
【通道(Channels)】
通道用于在不同的 goroutine 之间传递消息。通道本质上是引用类型,当复制或传递它们时,实际上传递的是对通道数据结构的引用。
【接口(Interfaces】
接口类型是一种抽象类型,定义了一组方法,但不会实现这些方法。接口内部存储的是指向实现了接口方法的值的指针和指向该类型信息的指针。
【函数(Functions)】
在 Go 中,函数也是一种引用类型。当把一个函数赋给另一个变量时,实际上是在复制一个指向该函数的引用。
【指针(Pointer)】
指针类型也是 Golang 中的一种基本类型,存储了值的内存地址。
指针类型可以指向任何值类型的数据,并且通过指针,可以在不同的函数之间共享和修改数据。
示例
//切片类型
//从形式上看切片就比数组少了个初始化长度
var slice1 []int=[]int{1,2,3}
//map类型
var obj = map[string]string{
"name":"zhansan",
}
//通道类型
var chan1 chan string;
//接口类型
type Dog interface{
add()
}
//函数
func add(a int,b int) int{
return a+b
}
//指针
var q *int= &age
引用类型有以下特点
1.存储的是指向数据的地址,而不是数据本身。
2.当引用类型的变量被赋值或作为函数参数传递时,实际上是将该地址复制一份,因此多个变量可能共享同一份数据。
3.引用类型的数据通常在堆上分配,即使变量本身在栈上。
4.引用类型的零值是 nil,一个未初始化的引用类型的变量将会是 nil,不指向任何内存地址。
常用类型表
值类型
| 类型名 | 类型中文名 | 零值 | 类型性质 | 字节长度 |
|---|---|---|---|---|
| bool | 布尔类型 | false | 值类型 | 1 |
| int, uint | 整数类型 | 0 | 值类型,根据操作系统变成32或64位 | 4或8 |
| int8, uint8 | 整数类型 | 0 | 值类型 | 1 |
| int16, uint16 | 整数类型 | 0 | 值类型 | 2 |
| int32, uint32 | 整数类型 | 0 | 值类型 | 4 |
| int64, uint64 | 整数类型 | 0 | 值类型 | 8 |
| float32 | 浮点数类型 | 0.0 | 值类型 | 4 |
| float64 | 浮点数类型 | 0.0 | 值类型 | 8 |
| complex64 | 复数类型 | 值类型 | 8 | |
| complex128 | 复数类型 | 值类型 | 16 | |
| byte | 字节类型 | 0 | 值类型,底层是uint8 | 1 |
| rune | 字符类型 | 0 | 值类型,底层是int32 | 4 |
| string | 字符串类型 | "" | 值类型 | 不定 |
| array | 数组类型 | 值类型 | 不定 | |
| struct | 结构体类型 | 值类型 | 不定 |
引用类型
| 类型名 | 类型中文名 | 零值 | 类型性质 |
|---|---|---|---|
| slice | 切片类型 | nil | 引用类型 |
| uintptr | 指针类型 | 引用类型,以存储指针的 uint32 或 uint64 整数 | |
| map | 映射类型 | nil | 引用类型 |
| channel | 通道类型 | nil | 引用类型 |
| interface | 接口类型 | nil | 引用类型 |
| function | 函数类型 | nil | 引用类型 |
布尔类型
概述
布尔类型的值只可以是常量 true 或者 false。
语法
var 变量名 bool;
举例
package main
import "fmt"
func main() {
//1.声明布尔类型的变量
var a bool
//2.查看零值
fmt.Print(a) //false
}
零值
false
数值类型
整数类型
int
# 第一位表示符号 正负
int8 有符号 8 位整型 (-128 到 127) ⻓度:8bit
int16 有符号 16 位整型 (-32768 到 32767)
int32 有符号 32 位整型 (-2147483648 到 2147483647)
int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
uint
uint8 ⽆符号 8 位整型 (0 到 255) 8位都⽤于表示数值
uint16 ⽆符号 16 位整型 (0 到 65535)
uint32 ⽆符号 32 位整型 (0 到 4294967295)
uint64 ⽆符号 64 位整型 (0 到 18446744073709551615)
语法示例
var a int8
var b int64
动态类型int
int单独出现是个动态类型,
var e int
//int是一个动态类型,根据操作系统计算得到
操作系统 32位 为: int <=> int32
操作系统 64位 为: int <=> int64
注意事项
golang是个对类型极为严格的语言,不会进行隐式类型转换,只要类型稍微不同就会认为是其他类型。
1.类型名不同即为不同类型
2.即使底层相同,但类型名不同也视为不同类型
3.类型别名与原类型也视为不同类型
//虽然都是整数类型,但他们实际上也是不同类型,是需要类型装换的
var a int8
var b int32
//报错
//a = b
//需要类型转换
a = int8(b)
浮点数类型
go没有float类型,只有以下两种浮点数类型
float32 32位浮点型数 #大约是3.4e38
float64 64位浮点型数 #大约是1.8e308
赋值
var f1 float32 = 3.14
var f2 float32 = 3.14
var f3 float32 = 3
//go会自己做转换变成浮点数
类型推导
//类型推断时会根据操作系统推导类型
e := 3.14
操作系统 32位 为:float32
操作系统 64位 为:float64
字符类型
golang中符号表示特别严格,
//单引号包裹的就是单个字符
''
//双引号包裹的就是字符串
""
byte类型
底层源码
type byte unit8
//byte就是unit8的类型别名
所以byte类型本质上是一个数字
作用
主要用来存放字符
赋值
// 主要适用于存放字符
var c byte
c = 'a'
//byte本质上是个数字,所以打印的是个数字
//byte用于存放ASCII码,是其他语言中的char类型
fmt.Println(c) //97
//想要打印字符,需要格式化为char格式
// %c 会把ASCII码转为码值对应字符
fmt.Printf("%c",c)
注意事项
由于本质上是个数字,所以是可以计算的
var k byte
k = 'a'+1 //98
// 98对应的ASCII码为字符 b
rune类型
底层源码
type rune int32
//由于byte类型的大小限制,他表示不了很多文字
//实际上就是ASCII码和万国码的区别
作用
用于存放字符,byte类型局限于ASCII码,不能表示中文。
举例
//byte的大小太小
//就是ASCII码和万国码的区别
var c2 rune
c2 = '幕'
fmt.Printf("%c",c2)
转义字符
| 转义字符 | 意义 | ASCII 码值(⼗进制) |
|---|---|---|
| \n | 换⾏ (LF) ,将当前位置移到下⼀⾏开头 | 010 |
| \r | 回⻋ (CR) ,将当前位置移到本⾏开头 | 013 |
| \t | ⽔平制表 (HT) (跳到下⼀个 TAB 位置) | 009 |
\\ | 代表⼀个反斜线字符\ | 092 |
\' | 代表⼀个单引号(撇号)字符 | 039 |
\" | 代表⼀个双引号字符 | 034 |
| ? | 代表⼀个问号 | 063 |
字符串类型
也被称之为string类型,与字符类型的区别:
1.字符类型用'',字符是指单个的
2.字符串类型用"",顾名思义,存放一个或多个字符
语法
var 变量名 string;
基本操作
对于字符串的更多操作将单独讲解,这里举例讲解通过下标访问字符串
// 字符串[下标] 能拿到对应字符
var str = "abc"
println(str[0])//对应ASCII码
fmt.Printf("%c",str[0])//a
//但是对应中文会有问题
var str1 = "好好学习"
println(str1[0])//乱码
//处理方式
str2 := []rune(str1)
println(str2[0])//对应码值
fmt.Printf("%c",str2[0])//好