>  기사  >  백엔드 개발  >  go에서 유형 비교 분석

go에서 유형 비교 분석

藏色散人
藏色散人앞으로
2021-06-21 16:09:533349검색

다음 튜토리얼 칼럼인 golang에서는 go의 유형 비교 분석을 소개하겠습니다. 도움이 필요한 친구들에게 도움이 되길 바랍니다!

개요

최근 인터뷰에서 면접관이 go의 유형 비교에 대해 질문한 바는 기본적으로 기초가 충분하지 않습니다. 인터넷에서 많은 정보를 읽고 제가 직접 간단한 요약도 해봤습니다. 하하!

go의 유형

먼저 go에 포함된 가장 기본적인 중앙 집중형 유형을 살펴보겠습니다

  • 기본 유형: Go의 가장 기본적인 유형에는 정수(int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, byte, rune 등), 부동 소수점 유형(float32, float64), 문자열(string도 a []rune 배열) 및 덜 일반적으로 사용되는 복합 유형(complex64/complex128). int、uint、int8、uint8、int16、uint16、int32、uint32、int64、uint64、byte、rune等)、浮点型(float32、float64)、字符串(string也是个[]rune数组)和比较不常用的复数类型(complex64/complex128)。
  • 复合类型:主要包括结构体数组
  • 引用类型:Slice、Map、Channel、指针
  • 接口类型:Error、io.Reader等

go作为强类型语言并不会和PHP等高级语言自动帮我们进行类型转换,所以我们在比较时必须用==两边的类型必须一致,即使他们底部类型一致也不行。看下面的代码

package main
import "fmt"
type A struct {
    Id int
}
type B struct {
    Id int
}
func main() {
    var a int
    var b int16
    // 编译报错:invalid operation a == b (mismatched types int and int16)
    fmt.Println(a == b)
    aStruct := A{Id:5}
    bStruct := B{Id:5}
    // 编译报错:invalid operation: aStruct == bStruct (mismatched types A and B)
    fmt.Println(aStruct == bStruct)
}

所以go不会帮我们做隐式转换,即使底层的类型一致,也不能比较。
接下来我们从不同的类型来分析是否可以进行比较。

基本类型

go的基本类型就比较简单,只要类型是一样的,那么他们就是可以比较的,举个栗子:

package main
import "fmt"
func main() {
    var a int = 0
    var b int = 1
    // 输出false
    fmt.Println(a == b)
}

不过基本类型中也要注意浮点型的比较就不像我们现实中的一样,比如0.1+0.2在计算中运行结果就不是0.3了,而是0.30000000000000004了

package main
import "fmt"

func main() {
    var a float64=0.1
    var b float64=0.2
    // 0.30000000000000004
    fmt.Println(a+b)
}

为什么会这样,可以看下draveness(https://github.com/draveness) 大佬的这篇文章https://draveness.me/whys-the...

复合类型

数组

面试中也经常会问到go数组和切片的区别。数组在go中是必须先确定长度的,也就是长度不能再去扩容。并且它是个值拷贝,做参数传到一个函数中被修改,那么外部的值还是一样的不变的。Slice则相反。那么数组是否可以比较呢,看下面的例子:

package main
import "fmt"
func main() {
    a := [2]int{1, 2}
    b := [2]int{1, 2}
    c := [2]int{1, 3}
    d := [3]int{1, 2, 4}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
    fmt.Println(a == d) // invalid operation: a == d (mismatched types [2]int and [3]int)
}

可以看出,相同长度的数组是可以比较的,而不同长度的数组是不能进行比较的。原因是什么呢?这是因为数组类型中,数组的长度也是类型的一部分,不同长度的数组那么他们的类型也就被认为不同的,所以无法比较。

结构体

同样的Struct也是一样的。Struct的比较也从内部类型开始比较,每一类型的值相等才是相等的。如下例子:

package main
import "fmt"
type A struct {
    id int
    name string
}
func main() {
    a := A{id:5,name:"123"}
    b := A{id:5,name:"123"}
    c := A{id:5,name:"1234"}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
}

那么可以理解成Struct结构体是可以比较的吗。我们再来看个例子:

package main
import "fmt"
type A struct {
    id int
    name string
    son []int
}
func main() {
    a := A{id:5,name:"123",son:[]int{1,2,3}}
    b := A{id:5,name:"123",son:[]int{1,2,3}}
    fmt.Println(a == b) // invalid operation: a == b (struct containing []int cannot be compared)
}

怎么又变成不可比较的呢?这就要看下面的引用类型了。

引用类型

上面中的例子结构体中带上切片就无法比较了,在go中SliceMap被定义成不能比较的类型。我们来看

如果Slice是可比较,那么用什么来定义是一样的切片呢?如果用地址,那么如果两个地址指向的Slice是一样的呢?这显然不合适。如果和数组一样的方式,那么我切片扩容了呢,就不相等了。所以长度和容量导致不好比较。虽然可以在语言层面解决这个问题,但是 golang 团队认为不值得为此耗费精力。所以Slice被当成不可比较。
同样的Map

복합 유형: 주로 구조배열을 포함합니다.

참조 유형: 슬라이스, 맵, 채널, 포인터.
  • 인터페이스 유형: 오류, io.Reader 등.
  • Go는 강력한 유형의 언어이므로 PHP와 같은 고급 언어로 유형 변환을 자동으로 수행하는 데 도움이 되지 않으므로 양쪽 유형을 비교할 때 ==를 사용해야 합니다. 하단 유형이 일관성이 있더라도 일관성을 유지하세요. 아래 코드를 보세요
package main
import "fmt"
type A struct {
    id int
    name string
}
func main() {
    a := &A { a : 1, b : "test1" }
    b := &A { a : 1, b : "test1" }
    c := a
    fmt.Println(a == b) // false
    fmt.Println(a == c) // true
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch3 := ch1
    fmt.Println(ch1 == ch2) // false
    fmt.Println(ch1 == ch3) // true
}

그래서 go는 기본 유형이 일관되더라도 비교할 수 없습니다.
다음으로 다양한 유형의 비교가 가능한지 분석해 보겠습니다.

기본 유형

🎜Go의 기본 유형은 유형이 동일하면 비교할 수 있습니다. 예: 🎜
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}
🎜그러나 기본에서 부동 소수점 유형의 비교에도 주의해야 합니다. 우리와는 다릅니다. 현실도 마찬가지입니다. 예를 들어 0.1+0.2를 계산하면 결과가 0.3이 아니라 0.30000000000000004🎜
var a interface{} = 0
var b interface{} = 2
var c interface{} = 0
var d interface{} = 0.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false
🎜이 글은 Draveness(https)에서 읽을 수 있습니다. //github.com/draveness) 글 https://draveness.me/whys-the...🎜🎜Composite type🎜

Array

🎜go 배열과 슬라이스의 차이점에 대해 인터뷰에서 자주 묻는 질문 . Go에서는 배열의 길이를 먼저 결정해야 합니다. 즉, 길이를 확장할 수 없습니다. 그리고 매개변수가 함수에 전달되어 수정되면 외부 값은 동일하게 유지됩니다. 슬라이스는 그 반대입니다. 따라서 배열을 비교할 수 있는지 여부는 다음 예를 참조하세요. 🎜
type A struct {
    a int
    b string
}

var a interface{} = A { a: 1, b: "test" }
var b interface{} = A { a: 1, b: "test" }
var c interface{} = A { a: 2, b: "test" }

fmt.Println(a == b) // true
fmt.Println(a == c) // false

var d interface{} = &A { a: 1, b: "test" }
var e interface{} = &A { a: 1, b: "test" }
fmt.Println(d == e) // false
🎜 같은 길이의 배열은 비교할 수 있지만 길이가 다른 배열은 비교할 수 없습니다를 알 수 있습니다. 이유는 무엇입니까? 이는 배열 유형에서는 배열의 길이도 유형의 일부이기 때문입니다. 길이가 다른 배열은 다른 유형으로 간주되므로 비교할 수 없습니다. 🎜

구조

🎜같은 구조는 같습니다. Struct의 비교도 내부 유형부터 시작되며, 각 유형의 값은 동일할 때만 동일합니다. 다음 예: 🎜
var a interface{} = []int{1, 2}
var b interface{} = []int{1, 2}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)
🎜그래서 Struct 구조가 유사하다는 것을 이해할 수 있습니다. 또 다른 예를 살펴보겠습니다. 🎜
var f *os.File

var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true

var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)
🎜는 어떻게 다시 비교할 수 없게 되었나요? 이는 아래 참조 유형에 따라 다릅니다. 🎜🎜참조 유형🎜🎜위 예제 구조에는 슬라이스가 포함되어 있으면 비교할 수 없습니다. go에서는 SliceMap이 비교 불가능한 유형으로 정의되어 있습니다. 살펴보겠습니다🎜🎜슬라이스가 비교 가능한 경우 동일한 슬라이스를 정의하는 데 무엇이 사용됩니까? 주소를 사용하는 경우, 두 주소가 가리키는 Slice가 동일하다면 어떻게 될까요? 이는 분명히 부적절합니다. 배열과 동일하면 슬라이스를 확장하면 동일하지 않습니다. 따라서 길이와 용량을 비교하기가 어렵습니다. 이 문제는 언어 수준에서 해결될 수 있지만 golang 팀은 노력할 가치가 없다고 믿습니다. 따라서 Slice는 비교할 수 없는 것으로 간주됩니다.
동일한 Map도 비교할 수 없는 유형으로 정의됩니다. 그렇다면 참조 유형은 비교할 수 없는 것인가요? 아니요, 예를 살펴보세요. 🎜
type ReadCloser interface {
    Reader
    Closer
}
🎜참조 유형 변수는 특정 변수의 메모리 주소를 저장합니다. 따라서 참조 유형 변수를 비교하면 두 참조 유형이 동일한 변수를 저장하는지 여부가 결정됩니다. 🎜🎜🎜동일한 변수라면 메모리 주소도 동일해야 하고, 참조형 변수도 동일해야 하며, "=="를 사용하여 true로 판단하세요.🎜🎜동일한 변수라면 메모리 주소도 달라야 합니다. , "==" 결과는 false입니다🎜🎜🎜인터페이스 유형🎜🎜Go 언어는 인터페이스 유형에 메소드 세트가 포함되어 있는지 여부에 따라 인터페이스 유형을 두 가지 범주로 나눕니다.🎜
  • 使用 runtime.iface结构体表示包含方法的接口
  • 使用 runtime.eface结构体表示不包含任何方法的 interface{} 类型
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}

所以我们可以得知,一个接口值是由两个部分组成的,即该接口对应的类型和接口对应具体的值。接口值的比较涉及这两部分的比较,只有当类型和值都相等(动态值使用==比较),两个接口值才是相等的。看个例子:

var a interface{} = 0
var b interface{} = 2
var c interface{} = 0
var d interface{} = 0.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false

ac类型相同(都是int),值也相同(都是0,基本类型比较),故两者相等。 ab类型相同,值不等,故两者不等。 ad类型不同,aintdfloat64,故两者不等。

type A struct {
    a int
    b string
}

var a interface{} = A { a: 1, b: "test" }
var b interface{} = A { a: 1, b: "test" }
var c interface{} = A { a: 2, b: "test" }

fmt.Println(a == b) // true
fmt.Println(a == c) // false

var d interface{} = &A { a: 1, b: "test" }
var e interface{} = &A { a: 1, b: "test" }
fmt.Println(d == e) // false

ab类型相同(都是A),值也相同(结构体A),故两者相等。 ac类型相同,值不同,故两者不等。 de类型相同(都是*A),值使用指针(引用)类型的比较,由于不是指向同一个地址,故不等。

不过需要注意的是,如果接口中类型是切片或者Map不可比较的类型,那么会直接报错的。看个例子:

var a interface{} = []int{1, 2}
var b interface{} = []int{1, 2}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)

ab的类型是切片类型,而切片类型不可比较,所以a == bpanic

接口值的比较不要求接口类型(注意不是动态类型)完全相同,只要一个接口可以转化为另一个就可以比较。例如:

var f *os.File

var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true

var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)

r的类型为io.Reader接口,rc的类型为io.ReadCloser接口。查看源码,io.ReadCloser的定义如下:

type ReadCloser interface {
    Reader
    Closer
}

io.ReadCloser可转化为io.Reader,故两者可比较。

io.Writer不可转化为io.Reader,编译报错。

总结

  • 可比较:int、ifloat、string、bool、complex、pointe、channel、interface、array
  • 不可比较:slice、map、function
  • 复合类型中如果带有不可比较的类型,那么该类型也是不可比较的。可以理解不可比较类型具有传递性。

위 내용은 go에서 유형 비교 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제