前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go之4种类型转换

go之4种类型转换

原创
作者头像
willsiom
修改2024-02-19 18:48:50
1K0
修改2024-02-19 18:48:50
举报
文章被收录于专栏:沈钦华的专栏

go语言是静态类型语言,对变量的类型有严格的要求,因而在日常编写代码过程中,经常需要对变量的类型进行转换操作。这里介绍下go语言支持的4种类型转行方法。

一、显式转换: T(x)

顾名思义,显示转换需要在代码中明确的编写转换语句,语法为: T(x),其中x为变量,T为转换的目标类型

代码语言:go
复制
package main

func main() {
	var a int = 123
	var b int64
	b = int64(a)  // 即使是从窄往宽转换(从int转int64),也必须显示的转换
	_ = b
}

这里需要注意的是,string和数值类型(int、float等等)的转换,需要通过strconv包来进行。strconv包的具体接口可以参照文档。

代码语言:go
复制
func main() {
	num := 123
	numStr := strconv.Itoa(num)
	fmt.Println(numStr)
}

二、隐式转换

与显式转换相对应的,不需要开发人员编写转换代码,由编译器自动完成。

常见的隐式转换有:

  • 数值常量初始化赋值、传参
  • 结构体类型转接口类型和interface{}
代码语言:go
复制
package main

import (
	"fmt"
)

type myBigInt int64  // 定义了一个新类型,myBigInt不是int64的别名,与int64是完全不同的类型

type People interface {  // 定义接口
	Say()
}

type Student struct {  // 定义实现接口的结构体
	Name string
}

func (s Student) Say() {
	fmt.Println("my name is ", s.Name)
}

func TestA(i interface{}) interface{} {  // 接收任意类似的函数
	return i
}

func main() {
	// 转接口
	var p People = Student{"li si"}  // 结构体自动转为接口
	p.Say()

	// 数值类型转换
	var n int64 = 123
	//var a myBigInt = n //这里必须显式转换,否则报错 Cannot use 'n' (type int64) as the type myBigInt
	var a myBigInt = 123 // 数值常量初始化赋值可以自动转

	// 具体类型转interface{},自动转换
	TestA(n)
	TestA(a)
	TestA(p)
}

三、类型断言:newT, ok := x.(T)

转换语法为:newT, ok := x.(T), x为待转换的变量,T为目标类型,表达式有2个返回值:newT为转换成功后接收新类型的变量,ok标识转换是否成功。表达式也可以不接收ok这个返回值,形如:newT := x.(T),在不接收ok返回值的情况下,一旦断言失败会直接抛出panic。

代码语言:go
复制
package main

import (
	"fmt"
)

type myBigInt int64

type People interface {
	Say()
}

type Student struct {
	Name string
}

func (s Student) Say() {
	fmt.Println("my name is ", s.Name)
}

func main() {
	var p People = Student{"li si"}
	s, ok := p.(Student) // 接口类型断言结构体
	if ok {
		s.Say()
	}else {
		fmt.Println("断言p为Student类型失败")
	}
/*
输出: my name is  li si
*/

	// 数值类型转换
	var n int64 = 123
	a := n.(myBigInt)  // n为具体类型,非interface类型,无法断言: Invalid type assertion: n.(myBigInt) (non-interface type int64 on the left)

	var a interface{} = 123 // 值为数值的interface{}类型在go内部一般使用float64来存储
	b, ok := a.(myBigInt) 
	if ok {
		fmt.Println("断言a为myBigInt成功: ", b)
	}else {
		fmt.Println("断言a为myBigInt失败")
	}
/*
输出: 断言a为myBigInt失败
*/
	c := a.(myBigInt) // 没有接收ok返回值,断言失败直接抛出panic。 panic: interface conversion: interface {} is int, not main.myBigInt
}

四、unsafe.Pointer强制转换

介绍这个方式之前,需要了解下go的指针。在go语言中有3类指针: 普通指针、uintptr、unsafe.Pointer。

  • 普通指针 (*T): 存储类型T的对象的地址,可以使用&(取地址)*(根据地址取值),但不可计算
  • uintptr:一个无符号的整型,可以保存一个指针地址,可以进行计算。uintptr无法持有对象,GC不把uintptr当指针,所以uintptr类型的目标会被回收
  • unsafe.Ponter: 可以指向任意类型的指针,不能进行计算,不能读取内存存储的值

我们知道,指针的本质是一个uint类型,存储对象的地址(地址其实也是一个数字),在C语言中指针可以任意的计算、指向,因而存在比较大的风险。go语言为了避免指针被滥用、误用的风险,对指针做了限制(如类型校验、不可计算、不可跨类型转换等)。但同时也留了一个口子,允许指针进行跨类型转换,这便是unsafe.Pointer(从unsafe可以看出这个指针不安全的,使用不当容易出事)。

三类指针的特性如下:

  • 普通指针(*T)不可计算、不可转换
  • unsafe.Pointer可以和任意类型指针转换(*T, uintptr, unsafe.Pointer)
  • uintptr可以计算

因而借助unsave.Pointer、uintptr,我们可以实现对普通指针进行计算和跨类型转换。这第四种类型转换便是借助unsafe.Pointer可任意转换的能力来实现。

  • 跨类型类型转换: newPtr := (*newT)(unsave.Pointer(ptr)) 先转为unsafe.Pointer,再转为目标类型指针 ptr -> unsafe.Pointer -> *newT
  • 指针计算: 先转为unsafe.Pointer,再转为uintptr,进行指针计算,再转为unsafe.Pointer,最后转为目标普通指针 ptr -> unsafe.Pointer -> uintptr -> 指针计算 -> unsafe.Pointer -> ptr
代码语言:go
复制
package main

import (
	"fmt"
	"unsafe"
)

type myBigInt int64


func main() {
	var n int64 = 123
	var ptr *int64 = &n

	fmt.Println(ptr)
    // 将ptr转为unsafe.Pointer,然后转为*myBigInt
	myBigIntPtr := (*myBigInt)(unsafe.Pointer(ptr)) 
	fmt.Println(myBigIntPtr, *myBigIntPtr)

}
/*
    输出:
    0xc0000a2000
    0xc0000a2000 123

*/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、显式转换: T(x)
  • 二、隐式转换
  • 三、类型断言:newT, ok := x.(T)
  • 四、unsafe.Pointer强制转换
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档