Go语言入门之变量、常量、指针以及数据类型
1.变量的声明和定义
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
关键字来声明变量
const a int = 1 // 正常定义 canst b = 2 // 无类型常量 定义后会通过隐式转换转为初始值对应的类型int
(2)常量生成器(枚举)
iota // 从0开始加1
例子:
// 显示奇数 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.指针
指针是一个变量,其值是另一个变量的内存地址
必须要先声明一个指针,然后才能使用它来存储任何变量地址
var p *int // var 指针名 指针类型 未初始化默认值为nil
指针的操作
&
是取地址操作符*
是取值操作符*数据类型
是指针类型
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
指针未初始化取值会报错
// 声明指针 此时a为nil var a *int // 这会报错,原因是此时a的值是nil,系统访问不到nil的地址值, // 我们需要将指针重新指向一个变量,或者为指针重新开辟地址 *a = 10
解决方案:
- 1.用
new
在内存中申请一个地址 - 2.重新赋予其其他变量的地址
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)整型
有符号整型
int // 范围不固定,可能32可能64 int8 int16 int32 int64 // 范围-2^(n-1) 到 2^(n-1)-1 其中n为位数
无符号整型
uint // 范围不固定,可能32可能64 uint8 uint16 uint32 uint64 // 范围0 到 2^n-1 其中n为位数 uintptr // 存放指针,底层编程需要
定义
var age int // 未初始化默认值为0
溢出问题
算术运算创建的值超出了可以用给定字节数表示的范围时,会产生整数溢出
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
能精确表示的正整数并不是很大。
float32 // 范围 约1.4e-45 到 约3.4e38 4字节 float64 // 范围约4.9e-324 到 约1.8e308 8字节
不同写法
1.声明的时候可以只写整数部分或者小数部分
var e = .71828 // 0.71828 var f = 1. // 1
2.很小或很大的数最好用科学计数法书写,通过 e 或 E 来指定指数部分
var avogadro = 6.02214129e23 // 阿伏伽德罗常数 var planck = 6.62606957e-34 // 普朗克常数
(3)布尔型
用
bool
定义,值只有true和false,不参与任何计算以及类型转换
var a bool // 未初始化默认值为false
(4)字符型
byte
(uint8
),代表了 ASCII 码的一个字符rune
(int32
),代表一个 Unicode 字符,处理中文等字符需要用
rune
本质上是int32
类型的别名类型,与int32
类型是完全等价一个字符串也可以被视为
rune
实例的集合,因此可用字符串字面量初始化一个rune
// 使用单引号 表示一个字符 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
文本
var name string = "张三" // 未初始化为""
string
底层
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.原生支持多行字符串打印
// 原生支持多行字符串打印,我们使用反引号实现 const str = `床前明月光 疑是地上霜 举头望明月 低头思故乡` fmt.Println(str) // 打印结果,中间的空格换行操作都会被打印呈现出来 /* 床前明月光 疑是地上霜 举头望明月 低头思故乡 */
5.类型转换
(1)显式类型转换
a := 5.0 b := int(a)
类型转换推荐从一个小的取值范围转换到一个大的取值范围,如果从大的取值范围转到小的取值范围则可能会出现
精度丢失
的情况
只有相同底层类型的变量之间可以相互转换,否则会出现编译错误,如不能将布尔强转为int
变相修改字符串内容
可以将字符串强转为
[]byte
或者[]rune
进行修改
例子:
str := "你是个狠人" strRune := []rune(str) strRune[3] = '狼' str2 := string(strRune) fmt.Println(str2) // 结果 // 你是个狼人
(2)隐式类型转换
隐式类型转换是编译器所为,在日常开发中,开发者并不会感觉到发生了变化,但是很常见
隐式转换报错发生在编译期,因此很容易发现并修改
以下是一些隐式类型转换发生的场景:
1.定义变量
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.函数调用时候转换
// 注意,次程序有编译期错误 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.函数返回时转换
// 注意,次程序有编译期错误 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)常用类型转换
整数转浮点数
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 }
整数转字符串
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表示十进制 }
整数转布尔
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 }
浮点数转换字符串
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)) }
浮点数转整数
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)) }
字符串转整型
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 }
字符串转浮点
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 }
字符串转布尔
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 }