数据类型
前言
与其它编程语言差不多,Go原生支持一些常见的数值型、字符串型等数据类型。
基本数据类型
数值类型
- 整数:int、uint、int8(1个字节)、uint8、int16(2个字节)、int32(4个字节)、int64(8个字节)
- 浮点数:float32、float64
- 复数:complex64、complex128
布尔类型
取值有false
和true
两种
字符串类型
- string
- rune
相较于C语言,Go语言原生支持字符串类型。
- 字符串类型的数据不可变,这个特性在多协程并发时能提供一定的安全性。
var s1 string = "hello"
s1[0] = 'w' // 错误。字符串内容不可变
s1 = "world" // ok
- 获取字符串长度的时间复杂度是常数时间。
- 使用反引号实现原生字符串,而不需要转义符。(除了反引号)
var s1 string = `!@#$%^&*()QWERTYUIOasdfagsds`
fmt.Println(s1) // !@#$%^&*()QWERTYUIOasdfagsds
- 原生采用Unicode字符集,避免源码在不同环境下显示乱码的可能。
指针
指针本质上就是内存地址,一般为内存中存储的变量值的起始位置。指针变量即存储内存地址的变量。
指针的优势在于,当我 们需要把大量的数据作为函数参数进行传递时,高效的方法不是传递数据本身,而是使用指向数据的指针作为函数的参数,这样就无需复制一个副本来进行操作,速度更快,内存占用更低。
在C/C++语言中,指针直接操作内存的特性使得 C/C++ 具备极高的性能,但指针偏移、指针运算和内存释放可能引发的错误也让指针编程饱受诟病。
Go语言限制了指针类型的偏移和运算能力,保留了指针高效访问的特性。
在Go语言中,指针包含以下三个概念:
- 指针地址
- 指针类型
- 指针取值
在程序运行的过程中,每一个变量的值都保存在内存中,变量对应的内存有其特定的地址。假设某一个变量的类型为 T,在Go语言中,我们可以通过取址符号 &
获取该变量对应内存的地址,生成该变量对应的指针。此时,变量的内存地址即生成的指针的值,指针类型为 *T
,称为 T 的指针类型,*
代表指针。
指针的默认值是nil
,可以通过判断nil
来确定指针变量是否有值。
指针的基础用法
// 取变量的内存地址
&varname
// 取内存地址的值
*memAddress
// 创建一个指针变量,默认值为nil
var p0 *int
// 声明一个变量的指针: var 指针名 *类型 = 初始化值
var a int = 7
var p1 *int = &a
// 创建指针类型: 指针名 := new(类型)
// 通过new(创建指针时,go编译器会申请内存空间,并将内存空间的值置为0)
p2 := new(float)
// 指针的指针
p1p1 := &p
// *&成对出现相当于互相取消,例如*&*&a就是a
// 指向数组的指针
arr1 := [3]int{1,2,3}
var p3 *[3]int = &arr1
// 指针数组
var p4 [3]*int // 创建能够存放三个指针变量的数组
指针变量声明后如果没有初始化就可能产生“野指针”。对野指针进行操作会导致panic
错误。
地址传参
Go语言中函数的参数都是按值进行传递的,即使参数是指针,也只是指针的一 个副本。习惯上把指针的函数参数称为地址参数,而非指针的函数参数成为值参数。
示例代码:
package main
import (
"time"
"fmt"
)
func change(arr [1024]int) {
// 值传参。参数为整数型数组
for i,v := range arr {
arr[i] = v * 2
}
}
func changeByAddress(arr *[1024]int) {
// 地址传参。参数为整型数组指针
for i,v := range *arr {
arr[i] = v * 2
}
}
func main() {
arr := [1024]int{}
for i:=0;i<1024;i++ {
arr[i] = i
}
start := time.Now()
sum := 0
for j := 0; j < 1000000; j++ {
change(arr)
sum++
}
end := time.Since(start)
fmt.Println("change(arr)执行1000000次耗时: ", end)
//fmt.Println(arr)
start = time.Now()
sum = 0
for j := 0; j < 1000000; j++ {
changeByAddress(&arr)
sum++
}
end = time.Since(start)
fmt.Println("changeByAddress(&arr)执行1000000次耗时: ", end)
//fmt.Println(arr)
}
如果打印arr的值,可以发现值传参函数并未修改arr的值,而地址传参后,arr的值有改变