数据类型:
计算机存储设备最小信息单位是位(bit),最小的存储单元是字节(byte),占用字节的不同,所表示能存储的数据长度不同。数据类型用来说明数据的数据的结构,便于后面定义变量、参数传递等。
基本数据类型:
类型 | 默认值 |
---|---|
整型 | 0 |
浮点型 | 0.0 |
字符串 | “” |
布尔类型 | false |
派生数据类型:
派生数据类型就是由基础数据类型演变而来的
- 指针、数组、结构体、channel、函数、切片、接口、Map
数据类型说明:
1KB=1024B,1MB=1024KB,1G=1024MB
类型 | 有无符号 | 名称 | 占用字节 | 说明 |
---|---|---|---|---|
bool | 有 | 布尔类型 | 1 | 只有true或false,默认是false |
byte | 无 | 字节型 | 1 | 等价于uint8,当要存储字符的时候可以byte |
rune | 有 | 字符类型 | 4 | 专用于存储unicode编码,等价于uint32 |
int | 有 | 整型 | 4或8 | 32位系统4个字节 64位系统8个字节int占用多大取决于操作系统 |
uint | 无 | 整型 | 4或8 | 32位系统4个字节 64位系统8个字节 |
int8 | 有 | 整型 | 1 | -128 ~ 127 |
int16 | 有 | 整型 | 2 | -32768 ~ 32767 |
int32 | 有 | 整型 | 4 | -2147483648 ~ 2147483647 |
int64 | 有 | 整型 | 8 | -9223372036854775808 ~ 9223372036854775807 |
uint8 | 无 | 整型 | 1 | 0 ~ 255 |
uint16 | 无 | 整型 | 2 | 0 ~ 65535 |
uint32 | 无 | 整型 | 4 | 0 ~ 4294967295(42亿) |
uint64 | 无 | 整型 | 8 | 0 ~ 18446744073709551615 |
float32 | 有 | 浮点型 | 4 | 小数位精确到7位 |
float64 | 有 | 浮点型 | 8 | 小数位精确到15位 |
complex64 | 有 | 复数类型 | 8 | 32 位实数和虚数 |
complex128 | 有 | 复数类型 | 16 | 64 位实数和虚数 |
uintptr | 有 | 整型 | 4或8 | 无符号整型,⾜以存储指针的uint32或uint64整数 |
string | 有 | 字符串 | utf-8字符串 |
占用字节数不同有什么区别:
数据类型占用不同的字节所表示的存储空间大小不同
有符号和无符号的区别:
有符号的,长度第一位会存类型,剩余的长度才是数据。无符号的全部都可以存数据。前面有
u
就是无符号,没有u
就是有符号
有符号和无符号如何选择:
如果只存整数就使用无符号的,因为范围更大,如果存的有负数就用有符号的。
能用小的就不要选择大的,比如年龄就用byte(0 ~ 255)
查看变量的数据类型:
fmt.Printf("%T", 变量名)
查看变量占用的字节大小:
fmt.Printf("%d", unsafe.Sizeof(num))// unsafe.Sizeof(num):返回指定变量占用的字节数
基本数据类型和引用类型区别:
基本数据类型:
int、float、bool、string、数组、结构体
引用类型:
指针、切片、map、管道、接口
区别:
基本数据类型的变量是直接存数据的,通常在栈中分配的
引用数据类型的变量是存的对象(内存地址),这个变量分配的空间才是存数据的,通常在堆中分配的。当没有引用变量的时候,会由GC回收变成垃圾
整型:
演示:
func main() {var age uint = -10 // 报错,不支持负数fmt.Println(age) var b byte = 1var r rune = 2fmt.Printf("数据类型是:%T,%T", b, r) //uint8,int32
}
浮点类型:
- 关于数学运算的包里默认都是用的float64,日常使用也尽量使用float64的就行了
- 浮点数可能会造成精度损失,64位的要比32位的更精准
演示:
func main() {var num float64 = 123.456789num2 := 123.456789 // 自动推导方式定义变量默认是float64类型fmt.Printf("%f\n", num) //123.456789fmt.Printf("%.2f\n", num) // 使用".数字"可以保留小数位数并四舍五入 123.46fmt.Printf("%T", num2)
}
布尔类型:
- 布尔类型也叫bool类型,布尔类型的取值要么是true,要么是false
- bool适合做逻辑运算,一般用在流程控制语句里
- 使用
bool
关键字进行定义,默认值为false
func main() {var result boolfmt.Println(result) // falseresult = true //修改默认值fmt.Printf("%t", result) //true
}
字符类型:
- 字符就是用’单引号’括起来的数据,在Go中没有char类型,如果要存储单个字符,一般使用byte保存。
- 字符可以使用Go转义字符修饰数据
- 如果需要保存的变量值大于255时使用byte会溢出,可以使用int代替byte保存数据
- Go语言的字符使用的是UTF-8编码标识Unicode文本,所以Go统一使用了UTF-8没有乱码的问题出现
- 字符的本质也是一个整数,直接输出的时候是按照对应的UTF-8编码的码值运算的,所以字符可以参与运算。
- 字符变量赋值后,通过
%c
可以打印出该数字对应的unicode字符
点击查看ASCLL码表
演示:
func main() {var b byte = 'a'var b byte = '我' // 报错,一个汉字占用3个字节,数据溢出fmt.Println("b=", b) // Println打印结果不是a,是97,a=97是ASCLL码表对应值fmt.Printf("%c\n", b) // 使用%c可以打印出具体存储的字符var a int = 'b' // 也可以使用int类型定义fmt.Println("a=", a) // 98fmt.Printf("%c\n", a)
}
字符串类型:
- "双引号"内的数据称为字符串,本质是一串固定长度字符连接起来的字符序列,但是在Golang里是由单个字节(byte)连接起来的
- 字符串中有一个隐藏的结束标志
\0
, \0是字符串的结束符,任何字符串末尾都会自动加上\0- 字符串一旦赋值就不能修改了,字符串是不可变的
- 字符可以使用Go转义字符修饰数据
- 反引号以字符串的原生形式输出,在反引号里的内容全部都是普通文本,包括换行和特殊字符,可以实现防止攻击、输入源代码的效果
func main() {var name string = "itzhuzhu哈哈嘿嘿"fmt.Printf("%T\n", name) // 打印字符串的数据类型fmt.Println("name=", name) // 打印字符串的数据值fmt.Println("长度为:", len(name)) // 输出为20,len:打印字符串的长度,长度不包含隐藏的\0,把所有的数据转换为字节,如果字符是中文,一个中文是3个字节fmt.Println(utf8.RuneCountInString(name)) // 12 打印的是Unicode码(万国码),中文/英文都算一个字节// 使用反引号会原样输出,编译器不会解析原始字符串内的数据str := `func save() {var age int = 10var a int = 1var b int = avar name stringfmt.Println(age)fmt.Println(name)fmt.Println(a, b)}
`str2 := "嘻嘻"str += "可以再次拼接字符串,也可以和其它字符串变量一起拼接" + str2fmt.Println("str=", str)
}
在golang中做字符串拼接的时候如果需要换行,需要把加号放在上一行,否则会报错
允许以索引号访问字节数组(并非字符),但不能获取数组元素地址
func main() {s := "abc"println(s[1])println(&s[1]) // 错误: cannot take the address of s[1]
}
使用for遍历字符串时,分byte和rune两种方式
func main() {s := "itzhuzhu"for i := 0; i < len(s); i++ { // byte,返回的是ASCLL码fmt.Printf("%d: [%c]\n", i, s[i])}fmt.Println("-------------------")for i, c := range s { // rune,返回数组索引号,以及Unicode字符fmt.Printf("%d: [%c]\n", i, c)}
}
引用类型:
- Go的引用类型特指指针、slice、map、channel、interface,相比数字、数组等类型,引用类型拥有更复杂的存储结构。除分配内存外,它们还需初始化一系列属性,诸如指针、长度,甚至包括哈希分布、数据队列等。
- 内置函数new依指定类型数据长度分配内存,返回指针。而引用类型则必须使用make函数创建,编译器将make转换为目标类型专用创建函数(或指令),以完成内存分配和属性初始化。
以切片语法(起始和结束索引号)返回子串时,其内部依旧指向原字节数组
func main() {s := "abcdefg"s1 := s[:3] // 从头开始,仅指定结束索引位置s2 := s[1:4] // 指定开始和结束位置,返回[start, end)s3 := s[2:] // 指定开始位置,返回后面全部内容println(s1, s2, s3)// reflect.StringHeader 和 string 头结构相同。// unsafe.Pointer用于指针类型转换。fmt.Printf("%#v\n", (*reflect.StringHeader)(unsafe.Pointer(&s)))fmt.Printf("%#v\n", (*reflect.StringHeader)(unsafe.Pointer(&s1)))
}
StringHeader源码
type StringHeader struct {Data uintptr // 存放指针,指向的是原数组Len int // 字符串的长度
}
函数参数传递方式:
**值类型:**基本数据类型、数组、struct
**引用类型:**指针、slice、map、channel、interface
值类型参数默认就是值传递,而引用类型参数默认就是引用传递,其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是拷贝的地址,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。
传递解析:
值类型默认就是值传递:变量直接存储值,内存通常在栈中分配
var name = "itzhuzhu" // func test(name string){}// 这里传递的name就是值传递,传过来的是"itzhuzhu"
引用类型默认就是引用传递:变量直接存储的是一个地址,这个地址中存的才是值,内存通常在堆中分配,当没有任何变量引用这地址时就会被GC回收
var name = "itzhuzhu" // func test(&name string){}// 这里传递的就是name的内存地址,地址中存的才是"itzhuzhu"
类型别名:
类型别名相当于给数据类型起了个外号,下面两种格式都可以用
Go1.9版本前定义格式
type 类型别名 类型
Go1.9版本后定义格式
type 类型别名 = 类型