Go语言入门之变量、常量、指针以及数据类型

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,值为trueflase
  • 字符类型: byterune
  • 字符串类型:string
  • 其他类型:数组指针结构体Channel函数切片anymap

(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)字符型

  • byteuint8),代表了 ASCII 码的一个字符
  • runeint32),代表一个 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
}
相关推荐
hkNaruto19 小时前
【P2P】【Go】采用go语言实现udp hole punching 打洞 传输速度测试 ping测试
golang·udp·p2p
入 梦皆星河19 小时前
go中常用的处理json的库
golang
海绵波波10721 小时前
Gin-vue-admin(2):项目初始化
vue.js·golang·gin
每天写点bug21 小时前
【go每日一题】:并发任务调度器
开发语言·后端·golang
一个不秃头的 程序员21 小时前
代码加入SFTP Go ---(小白篇5)
开发语言·后端·golang
基哥的奋斗历程1 天前
初识Go语言
开发语言·后端·golang
ZVAyIVqt0UFji1 天前
go-zero负载均衡实现原理
运维·开发语言·后端·golang·负载均衡
唐墨1231 天前
golang自定义MarshalJSON、UnmarshalJSON 原理和技巧
开发语言·后端·golang
老大白菜1 天前
FastAPI vs Go 性能对比分析
开发语言·golang·fastapi
千年死缓2 天前
golang结构体转map
开发语言·后端·golang