Go语言入门之变量、常量、指针以及数据类型
1.变量的声明和定义
go
var 变量名 变量类型
// 声明单变量
var age int // 定义int类型的年龄,初始值为0
// 声明多变量
var a, b int = 1, 2
// 声明变量不写数据类型可以自动判断
var a, b = 123, "hello"
// 变量声明另一种方式(只能声明局部变量)
a, b := 123, "hello" // 既声明又赋值,如果之前声明过再用这个则会报编译错误
// 交换相同类型变量的值
a, b = b, a
// 声明全局变量
var (
a int
b bool
)
// 假设有个numbers的函数返回了三个值,获取返回值的后两个
_,numb,strs := numbers()
fmt.Println(numb,strs)
注意:
- 除全局变量外 的其他变量声明 后必须使用,否则会编译错误。
- 当局部变量和全局变量名称相同时,会优先考虑局部变量
2.常量
变量定义后经过初始化便为常量,常量只有布尔,整型,浮点型,底层类型这些类型定义
Go语言常量特点
- 支持无类型常量(定义常量时不指定类型)
- 支持隐式类型转换
- 可用于实现枚举
(1)常量的定义
Go语言引入
const
关键字来声明变量
go
const a int = 1 // 正常定义
canst b = 2 // 无类型常量 定义后会通过隐式转换转为初始值对应的类型int
(2)常量生成器(枚举)
go
iota // 从0开始加1
例子:
go
// 显示奇数
const (
a = 2*iota + 1
b
c
d
e
f
)
fmt.Println(a, b, c, d, e, f)
// 输出:1 3 5 7 9 11
3.指针
指针是一个变量,其值是另一个变量的内存地址
必须要先声明一个指针,然后才能使用它来存储任何变量地址
go
var p *int // var 指针名 指针类型 未初始化默认值为nil
指针的操作
&
是取地址操作符*
是取值操作符*数据类型
是指针类型
go
var ptr *int // 定义指针,未初始化
a := 10 // 定义整型10
ptr = &a // 将a赋值给ptr
fmt.Println(ptr) // 打印的是指针地址:0xc00000a0a8
fmt.Println(*ptr) // 打印的是指针指向的地址值:10
*ptr = 20
fmt.Println(a) // 打印的是a的原地址值,已被指针修改,结果为:20
指针未初始化取值会报错
go
// 声明指针 此时a为nil
var a *int
// 这会报错,原因是此时a的值是nil,系统访问不到nil的地址值,
// 我们需要将指针重新指向一个变量,或者为指针重新开辟地址
*a = 10
解决方案:
- 1.用
new
在内存中申请一个地址 - 2.重新赋予其其他变量的地址
go
var a *int
// 初始化后的内存空间 其值为对应类型的零值
a = new(int)
*a = 10
fmt.Println(*a) // 打印10
var ptr *int // 定义指针,未初始化
b := 10 // 定义整型10
ptr = &b // 将a赋值给ptr
fmt.Println(*ptr) // 打印10
4.数据类型
- 数值类型:
整型
和浮点型
- 布尔类型:
bool
,值为true
和flase
- 字符类型:
byte
和rune
- 字符串类型:
string
- 其他类型:
数组
,指针
,结构体
,Channel
,函数
,切片
,any
,map
(1)整型
有符号整型
go
int // 范围不固定,可能32可能64
int8 int16 int32 int64 // 范围-2^(n-1) 到 2^(n-1)-1 其中n为位数
无符号整型
go
uint // 范围不固定,可能32可能64
uint8 uint16 uint32 uint64 // 范围0 到 2^n-1 其中n为位数
uintptr // 存放指针,底层编程需要
定义
java
var age int // 未初始化默认值为0
溢出问题
算术运算创建的值超出了可以用给定字节数表示的范围时,会产生整数溢出
go
func main() {
var a int8 = 127 // 根据上面定义,int8的最大值必须小于127,
a += 1 // 达到128,产生数据溢出
fmt.Println(a) // 输出结果-128
var b uint8 = 1
b -= 2 // 减去2为-1,但是uint范围是大于0的
fmt.Println(b) // 因此输出结果为255
}
注意: 此类问题常发生于循环语句结束时的条件判断,所以选择变量类型需谨慎
(2)浮点型
通常优先使用
float64
类型,默认值为0.0因为
float32
类型的累计计算误差很容易扩散,并且float32
能精确表示的正整数并不是很大。
go
float32 // 范围 约1.4e-45 到 约3.4e38 4字节
float64 // 范围约4.9e-324 到 约1.8e308 8字节
不同写法
1.声明的时候可以只写整数部分或者小数部分
go
var e = .71828 // 0.71828
var f = 1. // 1
2.很小或很大的数最好用科学计数法书写,通过 e 或 E 来指定指数部分
go
var avogadro = 6.02214129e23 // 阿伏伽德罗常数
var planck = 6.62606957e-34 // 普朗克常数
(3)布尔型
用
bool
定义,值只有true和false,不参与任何计算以及类型转换
go
var a bool // 未初始化默认值为false
(4)字符型
byte
(uint8
),代表了 ASCII 码的一个字符rune
(int32
),代表一个 Unicode 字符,处理中文等字符需要用
rune
本质上是int32
类型的别名类型,与int32
类型是完全等价一个字符串也可以被视为
rune
实例的集合,因此可用字符串字面量初始化一个rune
go
// 使用单引号 表示一个字符
var ch byte = 'A'
// 在ASCII 码表中,A 的值是 65,也可以这么定义
var ch byte = 65
// 65使用十六进制表示是41,所以也可以这么定义 \x 总是紧跟着长度为 2 的 16 进制数
var ch byte = '\x41'
// 65的八进制表示是101,所以使用八进制定义 \后面紧跟着长度为 3 的八进制数
var ch byte = '\101'
fmt.Printf("%c",ch) // A
(5)字符串型
一个字符串是一个不可改变的字节序列,可以包含任意的数据
因为字符串的字节使用UTF-8
编码标识Unicode
文本
go
var name string = "张三" // 未初始化为""
string
底层
go
type stringStruct struct {
str unsafe.Pointer // 指向了string底层的byte数组 占8字节
len int // 定义长度 占8字节
} // 所以string一共占16字节
特点
- 1.string类型的数据是不可改变的
- 2.零值可用,零值为
""
,长度0,占用16字节 - 3.获取长度的时间复杂度是
O(1)
级别,长度不可变,只需要读取内部长度len
就可以 - 4.支持通过+/+=操作符进行字符串连接
- 5.支持各种比较关系操作符:==、!= 、>=、<=、>和<
- 6.对非ASCII字符提供原生支持,都是以
utf-8
形式存在内存中的 - 7.原生支持多行字符串打印
go
// 原生支持多行字符串打印,我们使用反引号实现
const str = `床前明月光
疑是地上霜
举头望明月
低头思故乡`
fmt.Println(str)
// 打印结果,中间的空格换行操作都会被打印呈现出来
/*
床前明月光
疑是地上霜
举头望明月
低头思故乡
*/
5.类型转换
(1)显式类型转换
go
a := 5.0
b := int(a)
类型转换推荐从一个小的取值范围转换到一个大的取值范围,如果从大的取值范围转到小的取值范围则可能会出现
精度丢失
的情况
只有相同底层类型的变量之间可以相互转换,否则会出现编译错误,如不能将布尔强转为int
变相修改字符串内容
可以将字符串强转为
[]byte
或者[]rune
进行修改
例子:
go
str := "你是个狠人"
strRune := []rune(str)
strRune[3] = '狼'
str2 := string(strRune)
fmt.Println(str2)
// 结果
// 你是个狼人
(2)隐式类型转换
隐式类型转换是编译器所为,在日常开发中,开发者并不会感觉到发生了变化,但是很常见
隐式转换报错发生在编译期,因此很容易发现并修改
以下是一些隐式类型转换发生的场景:
1.定义变量
go
const a = 1
var b = 2
// 在我们定义变量时未指定类型,go会自动将数据类型转换为其初始值的类型
// 自定义一个数据类型
1type myInt int
//...省略main函数
a := 1 // 定义整型a
var b myInt = 100 // 定义myInt型b
c := a + b // 这样就会报错,因为a,b类型无法相加
// 如果将c定义为int或者myInt会不会隐式转换呢?不会
// 解决方案就是显式类型转换,将a或者b转化为对方类型
2.函数调用时候转换
go
// 注意,次程序有编译期错误
func demo(s string) {
fmt.Println(s)
}
// 3.这个函数接收了数据"你好",会自动转换成interface{}这个数据类型
func demo2(s2 interface{}) {
demo(s2) // 4.因此,s2现在是接口型数据却要传入demo中,所以会报错
}
func main() {
var s string = "你好" // 1.定义一个string字符串
demo2(s) // 2.传入到demo2函数,因为其数据类型是空接口的关系可以接收
}
3.函数返回时转换
go
// 注意,次程序有编译期错误
func demo(s string) {
fmt.Println(s)
}
// 3.接收了数据"你好",返回时会自动转换成interface{}这个数据类型
func demo2(s2 string) interface{}{
return s2
}
func main() {
var s string = "你好" // 1.定义一个string字符串
b := demo2(s) // 2.传入函数demo2并得到了一个返回值
demo(b) // 4.因此,b现在是接口型数据却要传入demo中,所以会报错
}
(3)常用类型转换
整数转浮点数
go
func main(){
var a int = 10
// 字面量转换方式
fmt.Printf("%f\n", float64(a)) // 10.000000
// 使用 strconv 包的 ParseFloat 函数转换
var b float64
b, _ = strconv.ParseFloat(strconv.Itoa(a), 64)
fmt.Printf("%f\n", b) // 10.000000
}
整数转字符串
go
func main(){
var a int = 10
// 方式1:Iota 入参只能为int
str := strconv.Itoa(a)
fmt.Printf("str: %s, type : %s\n", str, reflect.TypeOf(str))
// 打印结果:str: 10, type: string
// 方式2:
str = fmt.Sprintf("%d", a)
// 方式3:
str = strconv.FormatInt(int64(a), 10) // 10表示十进制
}
整数转布尔
go
func main() {
var a int = 10
fmt.Printf("a: %v\n", a > 10) // a: false
fmt.Printf("a: %v\n", a >= 10) // a: true
fmt.Printf("a: %v\n", a < 10) // a: false
fmt.Printf("a: %v\n", a != 10) // a: false
}
浮点数转换字符串
go
func main() {
var a float64 = 10.0
/*FormatFloat参数
参数1:要传入的值
参数2:格式化类型
参数3:保留的小数点(-1表示不对小数点格式化)
参数4:位数 64或者32
*/
str := strconv.FormatFloat(a, 'f', -1, 64)
fmt.Printf("str: %s, type : %s\n", str, reflect.TypeOf(str))
}
浮点数转整数
go
func main() {
var a = 1.12
// 方式1:直接强转,精度会丢失
b := int(a)
// 方式2:math包
b := int(math.Round(f))
fmt.Printf("num: %s, type : %s\n", b, reflect.TypeOf(b))
}
字符串转整型
go
func main() {
// 定义字符串
str := "12345"
/*ParseInt
参数1:string 类型
参数2:进制
参数3:位数
*/
num, err := strconv.ParseInt(str, 10, 64)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("num: %d, type: %s\n", num, reflect.TypeOf(num))
// 输出结果 num: 12345, type: int64
}
字符串转浮点
go
func main() {
// 定义字符串
str := "12.345"
/* ParseFloat
参数1:string 类型
参数2:位数
*/
a, err := strconv.ParseFloat(str, 64)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("float: %f, type: %s \n", a, reflect.TypeOf(a))
// 输出结果 float: 12.345000, type: float64
}
字符串转布尔
go
func main() {
str := "true"
boolValue, err := strconv.ParseBool(str)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("bool: %t, type: %s\n", boolValue, reflect.TypeOf(boolValue))
// bool value: true, type: bool
}