Go Type System(一)

Overview of Go Type System

  • Go由哪几种类型组成?

    • Built-in Basic Types
      • 包括:string, bool, float32, float64, complex64, complex128, uint8(byte), uint16, uint32, uint64, int8, int16, int32(rune), int64, int。共计17种类型。
    • Composite Types
      • 包括:pointer, function, interface, struct, map, slice, array, channel。共计8种类型。
    • Unsafe Pointer Types(由标准库中unsafe包提供)。
    • 综上,Go类型系统由(17 + 8 + 1) == 26种类型构成。
  • 什么是Type Literal?

    • 它是表示(denote)复合类型的一种形式。比如map[int]int, *int, []int等。
    • 它是Undefined Type,因为type T map[int]int,T本身就可以表示一种特定类型。
  • 什么是Type Declaration

    • 它也被称为Type DefinitionType Definition Declaration。格式为type NewTypeName SourceType
    • 不同的Type Declaration定义的多个类型一定是不同的类型。
    • NewTypeNameSourceType是两种不同的类型。
    • NewTypeNameSourceType拥有相同的Underlying Type。两者之间可以进行显式类型转换。
    • Type Declaration可以在函数内部使用。
  • 什么是Type Alias Declaration

    • 格式:type table = map[string]int
    • 它没有定义新的类型,仅仅给map[string]int起了一个别名。
    • Type Alias Declaration也可以在函数内部使用
  • 什么是Defined Types?什么是Undefined Types

    • 所有的Built-in Basic Types都是Defined Types
    • 通过Type Declaration定义的类型是Defined Types
    • 其它类型为Undefined Types
    • Defined Types等价于Named TypesUndefined Types等价于Unnamed Types
  • 什么是Underlying Types

    • Built-in Basic TypesUnderlying Type是它们本身
    • Unsafe Pointer TypesUnderlying Type是它们本身
    • Undefined TypesUnderlying Type是它们本身
    • type NewTypeName SourceType NewTypeName和SourceType拥有相同的Underlying Type
  • 什么是Value?

    • Value有多种表示方式,例如Value Literal(也被称为unnamed constants)、Named ConstantsVariablesExpressions
    • 每种类型都会有一个Zero Value,比如function, pointer, interface, map, slice, channelZero Valuenil,数字类型的Zero Value是0。
    • Value又可分为Untyped ValueTyped Valuevar i int8 = 10之所以是合法的,就是因为10就是Untyped Value。反证法:如果10是Typed Value(假如是int),那么这就违反了不同类型之间转换的规则。Untyped Values一般会有一个默认类型,例如""的默认类型是string, 10的默认类型是int,但是nil没有默认类型const X int = 10,该Named ConstantTyped Valueconst Y = 10,该Named ConstantUntyped Value
  • 什么是Value Part

    • 一个Value可能会由多个连续的内存块组成,它们被分为Direct PartIndirect Parts
    • 多个Value之间可能会共享Indirect Part,但是Direct Part一定是不共享的。所以Value Size的大小指的就是Direct Part
    • unsafe.Sizeof返回Value Size的大小
  • 哪些类型不支持比较

    • map, slice, functions
    • struct 的 field 类型属于以上三种类型
    • array 的 element 类型属于以上三种类型

指针(Pointers)

  • word的大小?

    • 在32位的操作系统下,word大小为4字节;在64位的操作系统下,word大小为8字节。
  • 什么是Base Type

    • 例如:**int // A non-defined pointer type whose base type is *int.。例如:type PP *Ptr // PP is a defined pointer type whose base type is Ptr.
  • 指针的类型转换规则?

    • 如果这两个类型的底层类型相同(忽略struct tag),则这两个类型之间可以进行显式类型转换。
    • 如果这两个类型中的某一个是Undefined Type(考虑struct tag),那么它们之间可以进行隐式类型转换。
    • 如果两个类型都是Undefined Type,如果它们的Base Type相同,显然它们是相同的类型;否则,如果Base TypeUnderlying Type相同,即可进行显式类型转换。
  • 哪些值是可以取地址的(addressable可写的)?

    • 变量;array的元素;slice的元素;struct的字段
  • 其它值为什么不可以取地址(只读的)?

    • bytes in string: 因为字符串是不可修改的。
    • map element:Go编译器为了采用更高效的算法,将会改变map中元素的地址。
    • dynamic value of interface valuesconstant valuesliteral valuespackage level functionsmethodsintermediate values 等。这些Value是不可被修改的,只能作为赋值表达式的右值。

Structs

  • 什么构成了Struct的标识?

    • field name, field type, field orderfield tag
    • 如果两个Struct Type Literal的这些元素都相同,则这两个Struct Type是相同的。
  • Struct类型转换规则。

    • 如果这两个类型的底层类型相同(忽略struct tag),则这两个类型之间可以进行显式类型转换。
    • 如果这两个类型中的某一个是Undefined Type(考虑struct tag),那么它们之间可以进行隐式类型转换。
  • 如何计算Struct的大小?

    • 某类型的起始地址一定是它的Alignment的整数倍。如果某类型的大小,占四个字节及以下,它的Alignment大小等于Type SizearrayAlignment取决于the type alignment of elementstructAlignment取决于the max type alignment of fields其它类型的alignment值是word
    • 某类型的大小(Type Size)一定是Type Alignment的整数倍。Type Size的大小等于Direct Part的大小。

Value Parts

// map types
type _map *hashtableImpl
// channel types
type _channel *channelImpl
// function types
type _function *functionImpl

type _slice struct {
    // referencing underlying elements
    elements unsafe.Pointer
    // number of elements and capacity
    len, cap int
}

type _string struct {
    elements *byte // referencing underlying bytes
    len      int   // number of bytes
}

type _interface struct {
    dynamicType  *_type         // the dynamic type
    dynamicValue unsafe.Pointer // the dynamic value
}

type _interface struct {
    dynamicTypeInfo *struct {
        dynamicType *_type       // the dynamic type
        methods     []*_function // method table
    }
    dynamicValue unsafe.Pointer // the dynamic value
}

Arrays, Slices and Maps in Go

  • keyelement类型是否有要求?

    • 对于array和slice来说,key必须是int类型;对于map来说key必须是Comparable(== 和 !=),所以slice, map, function不能作为key。
    • element可以是任意类型
  • blank value VS zero value

    • a blank slice: i = []int{},zero value: i = nili = ([]int)(nil)
    • a blank interface: i := map[int]int(nil); a zero-value interface: i = nil
  • Nested Composite Literals Can Be Simplified?

    • 它被限制在使用Value Literal的情况下;
    • 可以不指定element的类型;也可以不指定key的类型。
  • v[k]的注意事项?

    • 如果v是slice类型
      • k值太大或v==nil,都会导致panic
      • 支持v[x:y:z]操作(reslice),要求0 <= x <= y <= z <= cap(v),其中z不是必须的。
    • 如果v是map类型
      • 如果v == nil。如果v[k]作为赋值表达式的左值,会导致panic;如果作为右值,v[k]的值是element的Zero Value
      • 如果k是Interface类型,且dynamic type不支持比较,则产生panic。
  • 如何拷贝一个slice?

    • append是否会为新的slice构建新的Indirect Parts,完全取决于是cap和元素数量的比较。
    • 我们知道y=xy=x[:]操作都会产生一个新的slice。但由于它们共享Indirect Part,x或y的增删改都会影响到对方。
    • 通过y = append(x[:0:0], x)进行一次深度拷贝,使得x和y不再共享Indirect Part
  • Zero Value的进一步解读

    m := *new(map[string]int)   // <=> var m map[string]int
    fmt.Println(m == nil)       // true
    s := *new([]int)            // <=> var s []int
    fmt.Println(s == nil)       // true
    a := *new([5]bool)          // <=> var a [5]bool
    fmt.Println(a == [5]bool{}) // true
    

    所以m==nil,表示m的Direct Part等于0。

  • 修改map元素的注意事项?

    • 你可以通过m[k] = v的方式修改元素的值;
    • 你只能对Direct Part进行整体修改,不能只针对某部分修改,如果元素的类型是struct,m[k].x = 1是不合法的。同理元素类型是array时,也不能对array element进行修改。
  • for...range操作的特殊性?

    • 可以对nil map或nil slice进行遍历
    • 如果删除了未被遍历的元素,则该元素肯定不会被遍历
    • 如果创建了一个新的元素,则该元素可能会被遍历。
  • 删除map中的所有元素?

    for key := range m {
      delete(m, key)
    }
    
  • 删除map中的某个元素?

    for key := range m {
      if key.expired() {
          delete(m, key)
      }
    }
    

Strings

  • 字符串是不可修改的?

    • 字符串是不可被修改的,所以s[i] = 'k'是不合法的。
    • 这带来的好处是字符串的拷贝是高效的,a=b操作只涉及Direct Part部分的修改。注意,a=string(b)操作将会导致字符串的深度拷贝
    • 如果我们尽量使用a=b的模式进行字符串拷贝,字符串之间的比较操作将会很高效。Go只需要检查字符串的长度是否一致,以及Indirect Part对应的内存块的指针是否一致。
  • 字符串和[]byte之间的关系

    • 通过[]byte(str) 和 []rune(str)将字符串类型转换为slice;通过string(xxx)将slice转换成字符串。两者之间的拷贝是深度拷贝
    • 在内置函数appendcopy中,如果第一个参数是[]byte,第二个参数可以是string。
    • 在以下四种情况下,Go编译器会进行优化,防止深度拷贝。
      • a conversion (from string to byte slice) which follows the range keyword in a for-range loop.
      • a conversion (from byte slice to string) which is used as a map key in map element indexing syntax.
      • a conversion (from byte slice to string) which is used in a comparison
      • a conversion (from byte slice to string) which is used in a string concatenation, and at least one of concatenated string values is a non-blank string constant.
  • 字符串的连接操作有哪些?

    • fmt.Sprintfbytes.Bufferstrings.Builderstrings.Join+
  • 关于Unicode和UTF-8之间的关系?

    • Unicode是一个字符集,它为世界上所有的文字进行了编码。但是Unicode的单位是code point,大多数情况下,一个字符对应了一个code point,但是也存在一个字符对应多个code point的情况。
    • Code Point在Go语言中对应了rune类型。UTF-8或者UTF-16是一种对Code Point进行编码的手段。目前,UTF-8最为流行。
    • Go语言要求Go文件是UTF-8编码,string中存储的是UTF-8编码的内容。
    • If a slice rune element value is outside the range of valid Unicode code points, then it will be viewed as 0xFFFD, the code point for the Unicode replacement character. 0xFFFD will be UTF-8 encoded as three bytes (0xef 0xbf 0xbd).
    • Bad UTF-8 encoding representations will be converted to a rune value 0xFFFD. If erroneous UTF-8 is encountered, the character is set to U+FFFD and the index advances by one byte.

你可能感兴趣的:(Go Type System(一))