1.  
  2. 主页
  3.  / 
  4. Go语言视频教程课件
  5.  / 
  6. Go语言教程之基本数据类型

Go语言教程之基本数据类型

本文视频教程可从以下方式进入,感谢关注并对喜欢的视频一键三连。

可从 B 站搜索:”兰三石”。

或者直接点击此处 B 站视频传送门

Go语言为我们提供了丰富的数据类型,当然这些数据类型也不是说,完全Go语言特有,编程语言发展至今,基本上已经有了一套固有的数据类型模式,例如整型、浮点、布尔、字符串等等,下面咱们就详细介绍下Go语言里面各种各样的数据类型。

下面我们先对Go语言的类型进行下分类总览。

基本类型

  • 整数,数字类型,分为正数和负数,同时根据数字大小来再次区分类型。
  • 浮点数,小数类型,提供了两种精度的浮点数,分别是 float32 和 float 64,数位越高越精确。
  • 复数,由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag),分别是 complex128(64 位实数和虚数)和 complex64(32 位实数和虚数)。
  • 字符串,一组连续的字符,双引号包裹。
  • 字符类型,byte和rune类型,单个字符,可以是ASCII字符中的一个,也可以是一个UTF-8字符。
  • 布尔值,只有真或假两个值的一种数据类型。

聚合类型

  • 数组,一个指定类型的数据集合。
  • 结构体,任意个任意类型的变量组成了结构体类型。

引用类型

  • 指针,一个变量值对应的内存地址变量。
  • 切片,是一个拥有相同元素的可变长度序列。slice 看起来与数组很像,但本质上不同。
  • Map,由Key和Value组成的数据集合,或者描述为散列表的引用。
  • 函数,一组代码组成的代码块。
  • channel,数据之间进行通信的管道,在Go语言中常用于和goroutine结合使用。

接口类型

  • interface,实现面向对象的关键类型,同时在不确定需要使用的类型的时候,也可以使用interface。

整数

Go 的整型分为有符号和无符号整数。

无符号整数会分成 uint8、uint16、uint32、uint64,及特殊无符号整数 uint。

有符号整数会分成 int8、int16、int32、int64,及特殊有符号整数 int。

uint 和 int 的位数是不确定的,是根据操作系统来定的,32位操作系统,那么此数据类型对应的int32/uint32,若是64位操作系统,则对应的是int64/uint64,因此我们在使用这类数据类型的时候,就不能单纯把它们当成32位或者64位来使用,需考虑操作系统。

类型描述
uint8无符号 8位整型 (0 到 255)
uint16无符号 16位整型 (0 到 65535)
uint32无符号 32位整型 (0 到 4294967295)
uint64无符号 64位整型 (0 到 18446744073709551615)
int8有符号 8位整型 (-128 到 127)
int16有符号 16位整型 (-32768 到 32767)
int32有符号 32位整型 (-2147483648 到 2147483647)
int64有符号 64位整型 (-9223372036854775808 到 9223372036854775807)

还有一个更为特殊的无符号整数类型 uintptr ,为啥要单独拿出来说呢,是因为这个类型,在我们平时的工程开发过程中,相对于上面的整数类型来说,很少用到,基本上只有在特定的场景里才会用到这个类型,一般是unsafe.Pointer结合使用的。

unsafe.Pointer 表示任意类型且可寻址的指针值,可以在不同的指针类型之间进行转换。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    num := "5"
    numPointer := &num

    //f := (*float64)(numPointer)
    f := (*float64)(unsafe.Pointer(numPointer))
    fmt.Println(f)
}

我们要知道在 Go 中指针是不允许进行运算的,但 uintptr 可以,所以它的意义在于将其它类型的指针转换成它进行运算,然后再转换回原本的类型。

func main() {
    u := new(User)
    name :=(*string)(unsafe.Pointer(u))
    *name = "test"
    
    age := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(u)) + unsafe.Offsetof(u.Age)))
    *age = 18
    fmt.Println(u)
}

数字字面量语法

Go1.13版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制的格式定义数字。

还支持使用_进行数字的分割。

package main

import "fmt"

// 数字字面量

func main() {
    // 二进制方式, 0b开头
    n1 := 0b11111100101
    fmt.Println(n1)

    // 八进制,以0开头
    n2 := 03745
    fmt.Println(n2)

    // 十六进制,0x开头
    n3 := 0x7e5
    fmt.Println(n3)

    // 下划线分割数字
    n4 := 20_21
    fmt.Println(n4)
}

浮点数

浮点数其实就是小数的表达方式,分为float32和float64,数位越高越精准。

理论上来说,float32的精度为 6 ~ 8 位,float64 的精度为 15 ~ 17 位。

package main

import (
    "fmt"
)

func main() {
    // float32
    var a1, b1 float32 = 10, 3
    fmt.Println(a1 / b1)

    // float64
    var a2, b2 float32 = 10, 3
    fmt.Println(a2 / b2)
}

/*
 * 结果
 * 3.3333333  // float32
 * 3.3333333333333335  // float64
*/

浮点数的格式化打印。

package main

import (
    "fmt"
)

func main() {
    var f float64 = 123.456

    fmt.Printf("%fn", f) // 打印浮点数
    fmt.Printf("%Fn", f) // 跟 f 一样
    fmt.Printf("%gn", f) // 紧凑输出末尾无0
    fmt.Printf("%Gn", f) // 跟 g 一样
    fmt.Printf("%8.2fn", f) // 整体输出长度始终为 8,不足8位,则在前面补上空格,小数部分显示 2 位
}

浮点数在程序中,能高效的表示和计算小数,但是有一个问题,就是在表示和计算的过程中,可能会丢失精度。

在下面的例子中,从明面上来看,大家肯定都感觉会是 0.9对吧,但是其实是0.8999999999999999

package main

import "fmt"

func main() {
   var f1 float64 = 0.3
   var f2 float64 = 0.6
   fmt.Println(f1 + f2)
}

结果告诉我们,如果你写的程序,对小数的精确度要求很高的话,那么你就需要好好的来深入的了解下浮点数在计算机里面的存储方式以及计算方式。

像是做金融产品开发,或者资金相关的开发工作,如果运算出现问题,可能就会导致非常严重的后果。

造成这种精度损失原因是,一些浮点数转换成二进制后,是近乎无限的长度,计算机本身的空间是有限的,使用有限的空间存储无限的内容,是不太现实的,因此计算机就会使用一些有效的方式来处理这些数据,因此就造成了一部分的精度损失。

float32float64,这两种浮点型数据格式遵循IEEE 754标准: float32 的浮点数的最大范围约为 3.4e38,可以使用常量定义:math.MaxFloat32。 float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64

IEEE 754 浮点数标准是由电气电子工程师学会在1985年提出的,并且在提出后不断更新,该标准定义了浮点数的存储、计算、四舍五入及异常处理等规则。

前面介绍过内置的浮点数会有一定的精度损失,如果我们的实际业务不能接受这种精度的损失的话,那么就需要考虑使用math/big标准库来去满足我们的需求。

math/big标准库提供了三种数据类型big.Intbig.Floatbig.Rat。通过这三种类型我们可以实现更大数量级别的计算和存储,例如我们可以通过big.Float来实现浮点类型的更高精度,甚至说只要计算机资源足够就可以进行无限的精度提升。

package main

import (
   "fmt"
   "math/big"
)

func main() {
   // 内置的浮点数类型
   var a1, b1 float64 = 10, 3
   fmt.Println(a1 / b1)

   // big.Float 不配置精度的话,默认是 float64
   a2, b2 := big.NewFloat(10), big.NewFloat(3)
   fmt.Println(new(big.Float).Quo(a2, b2))

   // big.Float 精度配置100
   a3, b3 := big.NewFloat(10), big.NewFloat(3)
   a3.SetPrec(100)
   b3.SetPrec(100)
   fmt.Println(new(big.Float).Quo(a3, b3))
}

其实内置的基本类型就能满足我们日常大部分的开发需求,如果后续需要使用到超越int64或者float64的数据类型,可自行搜索一下math/big的更多用法即可,咱们这里就不过多介绍了。

复数

在计算机中,复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。

Go语言中复数的类型有两种,分别是 complex128(64 位实数和虚数)和 complex64(32 位实数和虚数),其中 complex128 为复数的默认类型。

复数的值由三部分组成 R + Ii,其中 R 是实数部分,I 是虚数部分,R 和 I 均为 float 类型,而最后的 i 是虚数单位。

package main

import "fmt"

func main() {
    var c1 complex64
    c1 = 1 + 2i

    var c2 complex128 = complex(1, 2)

    fmt.Println(c1)
    fmt.Println(c2)
}

字符串

字符串不仅在Go语言中,在其他语言中,也都扮演着非常重要的角色,不管是存储、传输还是日志打印等等,都离不开字符串这个数据类型,在Go语言中字符串跟整数、浮点数之类的类型是一样,都是原生内置的。字符串内部使用UTF-8编码,因此可以直接赋值非ASCII码的字符。

字符串的值是通过双引号(” “)或者反撇号(“)包裹的。

在Go语言中字符串是由一系列字符组成的,不能被修改,只能被访问。

字符串声明及赋值

单行字符串

package main

import "fmt"

func main() {
    // 声明字符串变量
    var username1 string
    var nickname1 string
    fmt.Println(username1, nickname1)

    // 声明并赋值
    var username2 string = "lanyulei"
    var nickname2 string = "三石"
    fmt.Println(username2, nickname2)

    // 短变量的方式声明并赋值
    username3 := "lanyulei"
      nickname3 := "三石"
    fmt.Println(username3, nickname3)
}

多行字符串

很多时候单行字符串是无法满足我们的需求的,因此我们需要声明并赋值多行的字符串。

但是需要注意的是,反引号间换行将被作为字符串中的换行,并且所有的转义字符均无效,文本将会原样输出。

package main

import "fmt"

func main() {
    // 多行字符串
    poetry := `床前明月光,
疑是地上霜。`
    fmt.Println(poetry)
}

字符串转义符

平时开发过程中,有一些符号可能跟程序用的符号有冲突,因此就可以通过转义的方式解决这种问题。

Go语言中字符串常见的转义符包含回车、换行、单双引号、制表符等。

转义符含义
r回车符(返回行首)
n换行符(直接跳到下一行的同列位置)
t制表符
'单引号
"双引号
\反斜杠

例如:

package main

import "fmt"

func main() {
    // 多行字符串
    poetry := "床前明月光,n疑是地上霜。n"李白""
    fmt.Println(poetry)
}

字符串工具函数介绍

在Go语言中,为我们提供了一个叫做string的库,包含字符串常用的操作。

下面介绍一下string中字符串常用的操作。

方法介绍
strings.Contains(s, substr string) bool判断是否包含某一个字符串
strings.ContainsAny(s, chars string) bool判断是否包含一个字符串中的任何一个字符
strings.Fields(s string) []string将字符串以空白符分割,返回一个切片
strings.Split(s, sep string) []string将字符串使用指定的字符串分割
strings.HasPrefix(s, prefix string) bool判断是否使用某个字符串开头
strings.HasSuffix(s, suffix string) bool判断是否使用某个字符串结尾
strings.Index(s, substr string) int返回s中substr的第一个实例的索引,如果s中不存在substr,则返回-1
strings.LastIndex(s, substr string) int返回s中substr的最后一个实例的索引,如果s中不存在substr,则返回-1
strings.Join(elems []string, sep string) string使用sep将elems这个切片内的所有元素都连接起来

在标准库strconv中,还有其他关于字符串与其他类型转换的方法。

下面介绍几个常用的。

方法介绍
strconv.Atoi(s string) (int, error)字符串转整数
strconv.Itoa(i int) string整数转字符串

其他常用的字符串操作。

方法介绍
len(v Type) int获取字符串长度
+ 或 fmt.Sprintf(format string, a …interface{}) string拼接字符串

字符类型

Go语言教程之基本数据类型

前面有过介绍,字符串其实就是一系列字符组成,反之,可以理解为字符串的每一个元素都叫做字符。

Go语言的字符有以下两种:

  • 一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。
  • 另一种是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。

byte 类型是 uint8 的别名,对于只占用 1 个字节的传统 ASCII 编码的字符来说,完全没有问题,例如 var ch byte = ‘A’,字符使用单引号括起来。

但是像中文这种复合字符串就不行了,就得使用 rune 类型。咱们演示一下。

package main

import "fmt"

func main() {
    name := "lanyulei 三石"

    // 遍历字符串获取每个byte类型的字符
    for i := 0; i < len(name); i++ {
        fmt.Print(string(name[i]))
    }

    fmt.Println()

    // 遍历字符串获取每个rune类型的字符
    for _, v := range name {
        fmt.Print(string(v))
    }
    fmt.Println()
}

用过上面的例子,我们遍历获取每个byte类型字符的时候,前面的字母都正常输出了,因为前面的字母在ASCII中就是代表着一个字节,但是后面的中文出现了乱码,那是因为中文的话是复合类型,占用了多个字节,一般是 3 ~ 4 个,但是我们仅输出一个字节,那么这个中文就不是完整的,因此就出现了乱码。

我们下面是获取rune类型的字符,因为Go本身就是支持UTF-8编码的,因此当遍历到中文的时候,就按照 rune 类型来进行遍历了。

布尔值

Go语言中以bool关键字进行声明布尔类型数据。

一个布尔类型的值只有两种:true 或 false。if 和 for 语句的条件部分都是布尔类型的值,并且==<等比较操作也会产生布尔型的值。

package main

import "fmt"

func main() {
    name := "lanyulei"

    fmt.Println(name == "lanyulei")
    fmt.Println(name != "lanyulei")
}

布尔值可以和 &&(AND)和 ||(OR)操作符结合,并且有短路行为,如果运算符左边的值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值,因此下面的表达式总是安全的:

package main

import "fmt"

func main() {
    usernameList := []string{}

    if len(usernameList) > 0 && usernameList[0] == "lanyulei" {
        fmt.Println(true)
    } else {
        fmt.Println(false)
    }
}

布尔值并不会隐式转换为数字值 0 或 1。如果需要使用这种功能的话,可以自己封装一个方法进行使用。

package main

import "fmt"

// 0 则是 false,非 0 则是true
func itob(i int) bool { return i != 0 }

func main() {
    i := 1
    fmt.Println(itob(i))
}

反之亦然。

package main

import "fmt"

func btoi(b bool) int {
    if b {
        return 1
    }
    return 0
}

func main() {
    b := false
    fmt.Println(btoi(b))
}

Go的布尔类型,布尔型无法参与数值运算,也无法与其他类型进行转换。

类型转换

Go语言中只有强制类型转换,没有隐式类型转换。该语法只能在两个类型之间支持相互转换的时候使用。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

func main() {
    // 整数转字符串
    var i int = 1
    s1 := strconv.Itoa(i)
    fmt.Println("整数转字符串: ", strconv.Itoa(i), reflect.TypeOf(s1))

    // 字符串转整数
    var s string = "5"
    i1, _ := strconv.Atoi(s)
    fmt.Println("字符串转整数: ", i1, reflect.TypeOf(i1))

    // 浮点数转整数
    f1 := 3.1415926
    fmt.Println("浮点数转整数: ", int(f1))

    // 整数转浮点数
    i2 := 1993
    fmt.Println("整数转浮点数: ", float64(i2))
  
    // 等等
}
这篇文章对您有用吗?

我们要如何帮助您?

发表评论

您的电子邮箱地址不会被公开。