15

【Go语言踩坑系列(一)】基本数据类型

 4 years ago
source link: https://studygolang.com/articles/27188
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

声明

本系列文章并不会关注语法使用层面,更关心可能在使用中会出现的问题、以及通过这些问题引起的一些思考。相信这个系列会成为很有价值的文章。

要点

本文只关注Go语言的基本类型:如整型、浮点型、常量相关的内容。字符串、数组和切片等高级类型会在下一篇文章中讲述。

初始化顺序:当前包级别变量 -> 导入外部包的init() -> 当前包内的init() -> main()。通常可将一个包导入但是不使用的方式,初始化某些配置数据。

下面这段代码会运行config包和model包下的init()方法:

import (
    "cmdb-bg/cmd"
    _ "cmdb-bg/config"
    _ "cmdb-bg/model"
)

零值

我们都知道,当我们仅仅声明一个变量、但未对其进行初始化的时候,Go会给每种变量类型赋一个零值:

  • 整型:0
  • 浮点型:0
  • bool型:false
func main() {
    var a int
    var b float64
    var c bool
    fmt.Println(a, b, c) // 0 0 false
}

赋值与类型推断

如果你之前已经使用了":="对某个变量进行了声明与初始化,如果你想再次为这个变量进行重新赋值,切记不要加":"

func main() {
    a := 1
    a := 2
    fmt.Println(a) // 报错:no new variables on left side of :=
    a = 2 // ok
}
func main() {
    var a int = 1
    for a >= 1 {
        a := 2  // 无限循环
        a = 2   // 正常执行
        a = a-2
        fmt.Println(a)
    }
}

Go中可以用如下方式高效交换两个变量的值:

func main() {
    a := 0
    b := 1
    a, b = b, a // 交换,不需要使用临时变量
    fmt.Println(a, b) // 1, 0
}
  • Go的new()返回的是一个地址,而不是值本身:
func main() {
    a := new(int)
    fmt.Println(a) // 0xc000016050
}

if赋值加判断复合语句的作用域:f的作用域会被限制在if大括号所包裹的代码块内。在if的外部并不能使用变量f:

func f1() error {
    if f, err := os.Open("abc"); err != nil {
        return err
    }
    fmt.Println(f) // 编译不通过: undefined: f
}

// 解决:
func f1() error {
    f, err := os.Open("abc")
    if err != nil {
        return err
    }
    f.Close() // ok
}

运算与类型转换

int和int32是不同类型,若要把int当成int32来使用,必须进行强制类型转换。其他类型同理。

类型断言的使用(暂作了解):

func main() {
   // a必须是空接口类型, 任何类型都是interface的实现类,当声明为interface{}时,可以赋值给他任意类型
   var a interface{}
   a = 2;
   // 类型断言会返回两个值
   v, ok := a.(int)
   // 如果变量a是断言的类型,ok为true,v为被断言变量的值。
   // 否则ok为false,v为断言类型的零值
   fmt.Println(v, ok) // 2 true
}

如果进行算术运算之后发生了溢出,那么Go会直接丢掉溢出的高位部分。

所有基本类型的值都是可以比较的(整型、浮点型),其他高级类型的比较,一部分需要遵循一定规则,而一部分高级类型是禁止比较的。

不同数据类型不能直接做运算。不像其他语言,Go语言没有隐式类型转换。要想强制对不同类型做运算,必须进行显式的强制类型转换。转换成同一种类型之后,才能做运算:

func main() {
    var a int8  = 100
    var b int16 = 100
    fmt.Println(a + b) //  invalid operation: a + b (mismatched types int8 and int16)
    fmt.Println(int16(a) + b) // ok
}

在进行强制类型转换时需要注意:当整数值的类型的有效范围由宽变窄时,会截掉一定数量的高位二进制数。与这个类似的还有把一个浮点数类型的值转换为整数类型值时,浮点数类型的小数部分会被全部截断:

func main() {
    var a int16 = 428 //00000001 10101100
    fmt.Println(int8(a)) // -84
    // 截断高8位为:10101100。Go中用二进制补码表示数值。转成原码为为 11010100 即十进制-84
}

浮点数的精度有限,尽量不要做浮点数运算结果的比较:

func main() {
    var f float32 = 16777216 // 1 << 24
    fmt.Println(f == f+1)  // false

    var a float32 = 1.23
    var b float32 = 1.25
    fmt.Println(a-b)   // -0.01999998
}

iota常量让用二进制位做标记更简单了。在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一:

const (
   FlagUp Flags = 1 << iota // 第一种标记
   FlagBroadcast            // 第二种标记
   FlagLoopback             // 第三种标记
   FlagPointToPoint         // 第四种标记
   FlagMulticast            // 第五种标记
)

下篇预告

【Go语言踩坑系列(二)】字符串


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK