Golang的基本数据类型|Go主题月

425 阅读6分钟

基本数据

整数

  • GO同时具备有符号整数和无符号整数

    • 有符号整数分四种大小:8位、16位、32位、64位,用int8、int16、int32、int64表示
    • 对应的无符号整数是uint8、uint16、uint32、uint64
    • 此外还有两种类型int和uint。在特定平台上,其大小与原生的有符号整数\无符号整数类型大小相等
    • rune类型是int32类型的同义词,这两个名称可以互换使用。同样byte类型是int8类型的同义词,强调一个值是原始数据而非量值
    • uintptr也是一种无符号整数,其大小不确定,但足以完整存放指针。主要用于底层编程,例如Go与C的接口程序
  • 二元运算符的优先级排序

    *   /   %   <<    >>    &   &^
    +   -   |   ^
    ==  !=  <   <=    >     >=
    &&
    ||
    

    分五大优先级

浮点数

  • Go具有两种大小的浮点数float32和float64,在math包中有这两个类型的最大值和最小值MaxFloat32、MaxFloat64
  • 浮点数可以通过Printf的谓词%g输出,或者也可以使用%e(有指数)或%f(无指数)来输出
  • math包中具有NaN(Not a Number),它表示数学上无意义的运算结果(如0/0),在数学运算中,我们倾向于将NaN作为一个信号值,但直接判断具体的计算结果是否为NaN可能会造成潜在错误,因为与NaN的比较总是不成立的(除了!=

复数

  • Go具有两种类型的复数complex64、complex128,二者分别由float32和float64构成。内置的complex函数根据给定的实部和虚部创建复数,而内置的real函数和imag函数则分别提取复数的实部和虚部:

    var x complex128 = complex(1, 2)  // 1+2i
    var y complex128 = complex(3, 4)  // 3+4i
    fmt.Println(x * y)                // "-5+10i"
    fmt.Println(real(x * y))          // "-5"
    fmt.Println(imag(x * y))          // "10"
    
  • 如果在浮点数或十进制整数后面紧接着写字母i,如2i,它就变成一个虚数,表示一个实部为0的复数,根据常量运算规则,复数常量可以和其他常量相加。

    x := 1 + 2i
    y := 3 + 4i
    
  • math/cmplx包提供了复数运算所需的库函数,例如复数的平方根函数和复数的幂函数

    fmt.Println(cmplx.Sqrt(-1)) // "(0+1i)"
    

布尔值

  • bool型的值或布尔值(boolean)只有两种可能:真(true)和假(false)
  • bool类型无法隐式转换为数值(0或1),反之也不行

字符串

  • 字符串是不可变的字节序列,它可以包含任意数据,包括0值字节

  • 不可变意味着两个字符串能安全地共用同一段底层内存,使得复制任何长度字符串的开销都低廉,例如

    s := "hello world"
    hello := s[:5]	// hello
    world := s[6:]	// world
    

    这里去获取s、hello、world的&s、&hello、&world,会发现他们的地址还是不同的。这是因为,Go中string的内存模型如下所示

    使用&s取到的地址只是string这个结构体的地址,并非底层内存的地址,这里通过反射可以获取到该地址。

    s := "hello world"
    hello := s[:5] // hello
    world := s[6:] // world
    
    fmt.Printf("s %v \n", (*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
    fmt.Printf("hello %v \n", (*reflect.StringHeader)(unsafe.Pointer(&hello)).Data)
    fmt.Printf("world %v \n", (*reflect.StringHeader)(unsafe.Pointer(&world)).Data)
    // out
    // s 5006394 
    // hello 5006394 
    // world 5006400 
    

    可以看到,hello和s内部指针指向的地址是相同的,他们是共用的一段内存地址

  • 通过s[i]可以访问到第i个字符,试图访问许可范围外的字节会触发宕机异常

  • s[i:j]可以生成一个新的字符子串

  • 字符串可以通过运算符做比较,如==和<;比较运算按字节进行,结果服从本身的字典排序

字符串字面量(string literal)

  • 字符串的值可以直接写成字符串字面量(string literal),形式上就是带双引号的字节序列:"Hello, 世界"

  • 因为Go的源代码总是按UTF-8编码,并且习惯上Go的字符串会按UTF-8解读,所以在源码中我们可以将Unicode码点写入字符串字面量

  • 转移字符

    \a	“警告”或响铃
    \b	退格符
    \f	换页符
    \n	换行符(指直接跳到下一行的同一位置)
    \r	回车符(值返回本行行首)会清除本行的内容
    
    // 例如
    fmt.Println("hello\rgo")	// 输出:go
    
    \t	制表符
    \v	垂直制表符
    \'	单引号(仅用于文字字符字面量'\'')
    \"	双引号
    \\	反斜杠
    \xhh	十六进制的转义字符,h是十六进制数字(大小写皆可),且必须是两位。
    \ooo	八进制的转义字符,必须使用三位八进制数字(0~7),且不能超过\377
    

Unicode

  • 在Go中使用rune来代表Unicode字符记号,这是一个int32类型的数据

UTF-8

字符串和字节slice

  • 四个标准包对字符串操作特别重要:bytes、strings、strconv、unicode
  • strings包提供了许多函数,用于搜索、替换、比较、修整、切分和连接字符串
  • bytes包也有类似的函数,用于操作字节slice
  • strconv包具备的函数,主要用于转换布尔值、整数、浮点数为与之对应的字符串形式,或者将字符串转化为布尔值、整数、浮点数
  • Unicode包有判别文字符号值特性的函数,如IsDigit、IsLetter、IsUpper、IsLower。转换函数ToUpper、ToLower将其转换成指定的大小写

常量

  • 使用 const 关键字来定义

    const pi = 3.14159	// 近似值;math.Pi是更准确的近似
    

常量生成器iota

  • 常量的声明可以使用常量生成器iota来生成,它创建一系列相关值,而不是逐个显式的写出。从0开始取值,逐项加1

    type Weekday int
    const (
    	Sunday Weekday = iota
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
    )
    
    const (
        _ = 1 << (10 * iota)
        KiB	// 1024 = 1 << 10
        MiB	// 1048576 = 1 << 100
        GiB	// 1073741824 = 1 << 1000
        TiB	// 
        PiB
        EiB
        ZiB
        YiB
    )
    

无类型常量

  • Go中的许多常量并不从属某一具体类型,编译器将这些从属类型待定的常量表示成某些值,这些值比基本类型的数字精度更高,且算数精度高于原生的机器精度。借助推迟确定从属类型,无类型常量不仅能暂时维持更高的精度,与类型已确定的常量相比,它们还能写进更多表达式而无需转换类型。例如:

    fmt.Println(YiB/ZiB)	// 1024