• 周六. 7 月 27th, 2024

    Go语言数据类型

    root

    10 月 13, 2019 #Golang

    2.1 变量

    运行时内存分配操作会确保变量自动初始化为二进制零值
    如显式初始化值,可省略变量类型,由编译器推断
    退化赋值的前提条件是: 最少有一个新变量被定义,且必须是同一作用域
    func main() {
        x := 100 //简短模式声明并初始变量
        println(&x)
        
        x := 200         //错误: no new variables on left side of :=
        println(&x, x)
    }
    
    func main() {
        x := 100
        println(&x, x)
        {
            x, y := 200, 300    //不同作用域,全部是新变量定义
            println(&x, x, y)
        }
    }
    
    
    简短模式有些限制
    • 定义变量,同时显式初始化
    • 不能提供数据类型
    • 只能用在函数内部
    在处理函数错误返回值时,退化赋值允许我们重复使用err变量,这是相当有益的
    package main
    
    import ( 
        "log"
        "os"
    )
    
    func main() {
        f, err := os.Open("/dev/random")
        ...
        buf := make([]byte, 1024)
        n, err := f.Read(buf)       //err退化赋值,n新定义
        ...
    }
    
    在进行多变量赋值时,首先计算出所有右值,然后再依次完成赋值操作
    func main() {
        x, y := 1, 2
        x, y = y+3, x+2 //先计算出右值y+3、x+2,然后再对x,y变量赋值
        println(x, y)
    }
    
    编译器将未使用局部变量当作错误。

    2.2 命令

    命名建议

    • 以字母或下画线开始,由多个字母、数字和下画线组合而成。
    • 区分大小写。
    • 使用驼峰(camel case)拼写格式
    • 局部变量优先使用短名
    • 不要使用保留关键字
    • 不建议使用预定义常量、类型、内置函数相同的名字
    • 专有名词通常会全部大写。
    GO有个名为”_”的特殊成员,通常作为忽略占位符使用,可作表达式左值,无法读取内容
    import "strconv"
    func main() {
        x, _ := strconv.Atoi("12")  //忽略Atoi的err返回值
        println(x)
    }
    

    2.3 常量

    const x, y int = 123, 0x22
    const s = "hello, world!"
    const c = '我' //rune (unicode code point)
    const (
        i, f = 1, 0.123 //int, float64(默认)
        b  = false
    )
    
    可在函数代码块中定义常量,不曾使用的常量不会引发编译错误
    func main() {
        const x = 123
        println(x)
        const y = 1.23  //未使用,不会引发编译错误
        {
            const x = "abc" //在不同作用域定义同名常量
            println(x)
        }
    }
    
    常量值也可以是某些编译器能计算出结果的表达式,如unsafe.Sizeof,len,cap
    import "unsafe"
    const (
        pstrSize = unsafe.Sizeof(uintptr(0))
        strSize = len("hello, world!")
    )
    
    常量组中如不指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同.
    import "fmt"
    func main() {
        const (
            x uint16 = 120
            y       //与上一行x类型,右值相同
            s = "abc"
            z       //与s类型,右值相同
        )
        fmt.Printf("%T, %v", y, y)  //输出类型和值
        fmt.Printf("%T, %v", z, z)
    }
    
    GO借助iota标识符实现一组自增常量值来实现枚举类型
    const (
        x = iota //0
        y        //1
        z        //2
    )
    const (
        _ = iota    //0
        KB = 1 << (10*iota) // 1 << (10*1)
        MB                  // 1 << (10*2)
        GB                  // 1 << (10*3)
    )
    
    如中断iota自增,则必须显式恢复。 且后续自增值按行序递增,而非C enum那般按上一取值递增
    const (
        b = iota        //0
        b               //1
        c = 100         // 100
        d               // 100
        e = iota        // 4 (恢复iota自增, 计数包括c, d)
        f               // 5
    )
    
    在实际编码中,建议用自定义类型实现用途明确的枚举类型。但这并不能将取值范围限定在预定义的枚举值内
    type color byte //自定义类型
    
    const (
        black color = iota  //指定常量类型
        red
        blue
    )
    
    func test(c color) {
        println(c)
    }
    
    func main() {
        test(red)
        test(100)       //100 并未招出color/type类型取值范围
        
        x := 2
        test(x) //错误: cannot use x (type int) as type color in argument to test
    }
    
    变量在运行期分配存储内存(非优化状态),常量通常会被编译器在预处理阶段直接展开。作为指令数据使用

    2.4 基本类型

    类型长度默认值说明
    bool1false
    byte10uint8
    int,uint4, 80默认整数类型,依据目标平台,32或64位
    int8, uint810-128~127, 0~255
    int16, uint1620-32768~32767, 0~65535
    int32, uint3240-21亿~21亿, 0~42亿
    int64, uint6480
    float3240.0
    float6480.0默认浮点数类型
    complex648
    complex12816
    rune40Unicode Code Point, int32
    uintptr4, 80足以存储指针的uint
    string“”字符串,默认值为空字符串,而非NULL
    array数组
    struct结构体
    functionnil函数
    interfacenil接口
    mapnil字典,引用类型
    slicenil切片,引用类型
    channelnil通道,引用类型
    标准库math定义了各数字类型的取值范围
    标准库strconv可在不同进制(字符串)间转换
    import "strconv"
    func main() {
        a, _ := strconv.ParseInt("1100100", 2, 32)
        b, _ := strconv.ParseInt("0144", 8, 32)
        c, _ := strconv.ParseInt("64", 16, 32)
        println(a,b,c)
        
        println("0b"+strconv.FormatInt(a, 2))
        println("0"+strconv.FormatInt(a, 8))
        println("0x"+strconv.FormatInt(a, 16))
    }
    
    在官方的语言规范中,专门提到两个别名
    byte alias for uint8
    rune alias for int32
    
    别名无须转换可直接赋值

    2.5 引用类型

    引用类型特指slice,map,channel
    内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和初始化方式。
    引用类型则必须使用make函数创建,编译器会将make转换为目标类型专用的创建函数(或指令)
    func mkslice() []int {
        s := make([]int, 0, 10)
        s = append(s, 109)
        return s
    }
    func mkmap() map[string]int {
        m := make(map[string]int)
        m["a"] = 1
        return m
    }
    func main() {
        m := mkmap()
        println(m["a"])
        
        s := mkslice()
        println(s[0])
    }
    
    new函数也可以为引用类型分配内存,但这是不完整创建。

    2.6 类型转换

    除常量,别名类型以及命名类型外,Go强制要求使用显式类型转换。
    Go不支持操作符重载
    如果转换的目标是指针、单向通道或没有返回值的函数类型, 那么必须使用括号,以避免造成语法分解错误
    func main() {
        x := 100
        p := *int(&x)   //错误 cannot convert &x (type *int) to type int
                        // invalid indirect of int(&x) (type int)
        println(p)
    }
    
    正确的做法是用括号, 让编译器将*int解析为指针类型
    (*int)(p)   如果没有括号    *(int(p))
    (<-chan int)(c)             <-(chan int(c))
    (func())(x)                 func() x
    func()int(x)    -> 有返回值的函数类型可省略括号,但依然建议使用
    (func()int)(x)  使用括号后,更易阅读
    

    2.7 自定义类型

    使用关键字type定义用户自定义类型,包括基于现有基础类型创建,或者是结构体,函数类型等。
    type flags byte
    const (
        read flags = 1 << iota
        write
        exec
    )
    func main() {
        f := read | exec
        fmt.Printf("%b\n", f)
    }
    
    即便指定了基础类型,也只表明它们有相同底层数据结构,两者间不存在任何关系,属完全不同的两种类型。除操作符外,自定义类型不会继承基础类型的其他信息(包括方法)。不能视作别名,不能隐式转换,不能直接用于比较表达式

    未命名类型

    数组,切片,字典,通道等类型与具体元素类型或长度等属性有关,故称作未命名类型。
    具有相同声明的未命名类型被视作同一类型
    • 具有相同基类型的指针
    • 具有相同元素类型和长度的数组
    • 具有相同元素类型的切片
    • 具有相同键值类型的字典
    • 具有相同数据类型及操作方向的通道
    • 具有相同字段序列(字段名,字段类型,标签,以及字段顺序)的结构体
    • 具有相同签名(参数和返回值列表,不包括参数名)的函数
    • 具有相同方法集(方法名,方法签名,不包括顺序)的接口(interface)
    未命名类型转换规则
    • 所属类型相同
    • 基础类型相同,且其中一个是未命名类型。
    • 数据类型相同,将双向通道赋值给单向通道,且其中一个为未命名类型。
    • 将默认值nil赋值给切片,字典,通道,指针,函数或接口
    • 对象实现了目标接口。

    root

    发表回复