Go从入门到精通——数据类型

Go 数据类型

本章介绍如下内容: 

  • 数据类型是什么?
  • 区分静态类型和动态类型
  • 使用布尔类型
  • 理解数值类型
  • 检查变量的类型
  • 类型转换

  Go 是一种静态类型语言,而静态类型是一个必须理解的概念。如果您没有接触过静态类型语言,这个概念的理解尤为重要。

1.1 数据类型是什么

  数据类型让编程语言、编译器、数据库和代码执行环境知道如何操作和处理数据。例如,如果数据类型为数字,通常可对其执行数学运算。编程语言和数据库常常根据数据类型赋予程序员不同的功能和性能。大多数编程语言还提供了用于处理常见数据的标准库,而数据库提供了查询语言,让程序员能够根据底层数据类型来查询数据以及与之交互。无论数据类型是否被显示地声明,它们都是重要的编程和计算结构。

1.2 区分静态类型和动态类型

  所谓强类型语言,指的是错误地使用了类型时,编译器将引发错误;所谓动态类型(也叫松散类型或弱类型)语言,指的是为了执行程序,运行时会将一种类型转换为另一种类型,或者编译器没有实现类型系统。哪种语言更好呢?这存在很大争议,计算机科学家看重强类型语言的正确性和安全性,而其他人则看重动态语言的简单性和开发速度。

  下面是静态类型语言的一些优点:

  • 性能高于动态类型语言。
  • Bug 通常会被编译器发现。
  • 代码编辑器可提供代码不全和其他功能。
  • 数据完整性更好。

  下面是动态类型语言的一些优点:

  • 使用动态类型语言编写软件的速度通常更快。
  • 无须为执行代码而等待编译器完成编译。
  • 动态类型语言通常不那么死板,因此有些人认为变更代码更容易。
  • 有些人认为动态类型语言门槛更低。

  在 Go 中,程序员可显示地声明类型,也可让编译器推断类型。这里我们将显示地声明类型:

  从sayHello函数的参数声明可知,这个函数接受一个类型为 string 的参数;这个函数的返回值也是字符串。因此,编译这个程序时,编译器将检查传递给这个函数的参数是否是字符串;如果不是,编译器将发生错误。这正是我们希望的,因为这意味着错误可能根本不会让用户遇到。 

  Python 也是强类型语言,我们再看看 Python 的函数,它接受两个值,将它们相加并返回结果:

>>> def func(a,b):
...     return a + b;
... 

  给这个函数提供两个数字时,它能够正确地运行:

  然而,如果向它传递一个数字和一个字符串呢?

 

  我们再尝试弱类型语言 Javascript 函数,它也接受两个值,将它们相加并返回结果。

var addition = function(a,b) {
    return a,b;
};

  如 python 函数一样,我们这里也给这个函提供两个数字时,看看它能够正确地运行吗?

  给这个 Javasscript 函数提供两个数字数值的参数时候,是能够正常地运行的。现在我们再向它传递一个数字和一个字符串,看看结果如何:

  可以看的到, 执行 Javascript 函数后,虽然也可以的到结果,但是结果很奇怪。

  在这种情况下,这个函数返回一个字符串。怎么会这样呢?虽然 JavaScript 有类型的概念,但其类型使用规则非常宽松。在这个示例中,Javascript 对数字值执行类型转换,将其转换为字符串,因此返回字符串 7eight。Javascript 提供的这种灵活性虽然很有吸引力,但可能导致微妙乃至灾难性的 Bug。

  上述的 Javascript 函数 myFunction 函数可能返回一个字符串,也可能返回一个整数。如果传递给它的值至少有一个字符串,返回的就是字符串。如果这个返回值被插入到需要整数的数据库字段中,将引发错误。更糟糕的是,这种错误发生在运行阶段,这意味着它将影响使用程序的用户。这种错误除非得到妥善处理,否则可能导致程序崩溃。

  而在使用 Go 语言编写的函数中,对参数和返回值的类型都做了声明。

  下面我们也运行下 Go 语言的情况,如 Python 函数一样,我们这里也给这个函提供两个数字时,看看它能够正确地运行吗?

  结果是可以正确地运行。如果我们传入字符串试试呢?

 

  尝试运行程序后,发生了报错。报错信息为:".\int+int.go:14:28: cannot use "8" (type untyped string) as type int in argument to myFunction",原因是因为在需要 int 类型的地方使用了字符串。

1.3 使用布尔类型

  对类型有了基本认识后,就可以探索 Go 是如何实现一些基本数据类型了。首先来看布尔类型。布尔值只能为 true 或 false。虽然有些语言允许使用值 1 和 0 来表示 true 和 false,但 Go 语言不允许。可像下面这样声明布尔变量:

var a bool

  如果没有给布尔变量赋值,它将默认为 false:

  布尔变量可在声明后重新赋值,它们是很有用的编程元素:

 1.4 理解数值类型

  对编程来说,数值不可或缺。然而,如果您没有计算机科学或数学方面的知识,可能对有些术语感到迷惑。您可能听说过浮点数、整数、无符号整数、8位、64位、bigint、smallint、tinyint,这些都是整型(数值)类型。要明白这些术语的含义,必须知道数字在计算机内部是以二进制位的方式存储的。二进制位就是一些列布尔值,取值要么为1,要么为0。1位可表示1或0,对于4位整数,下面对其二进制位和十进制位表示做了比较。从该表可知,4位可表示16个不同的数字:

4位的无符号整数

二进制 十进制
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
二进制 十进制
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
二进制 十进制
1110 14
1111 15

 

 

 

 

 

 

 

 

 

1.4.1 带符号整数和无符号整数

  对于带符号整数,需要使用一位来表示符号,这通常是符号 - 。上表列出了无符号的 4 位整数,其取值范围为 0~15。带符号整数可正可负,因此 4 位带符号整数的取值范围为 -8 到 7。

4位的无符号整数

二进制 十进制
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
二进制 十进制
0111 7
1000 -8
1001 -7
1010 -6
1011 -5
1100 -4
1101 -3
二进制 十进制
1110 -2
1111 -1

 

 

  在 Go 语言中,声明整型变量的方式如下:

var  i int =3

  类型 int 表示带符号整数,因此可正科负。根据计算机的底层体系结构,int 可能是 32 位的带符号整数,也可能是 64 位的带符号整数。除非要处理的数字非常大,或者非常在乎性能,否则只需要使用 int,而无须关心编译器是如何做的。

1.4.2 浮点数

  计算语言中将数学中的小数称作浮点数,这是由小数在计算机中的表达形式而产生的叫法(通过小数点的浮动来表示更大范围小数)。

  在 Go 语言中常用的浮点数有两种:float32 和float64。其中,float32 类型的浮点数由 32 个二进制位组成的,而 float64 则是由 64 个二进制位组成的。显而易见,float64 类型的浮点数可以表达的数字范围更大或者精度更高。在实际应用中,我们可以自行选择使用 float32 还是 float64 作为变量类型,唯一需要考虑的是 Go 语言标准库或第三方库中的函数要求使用哪种类型。

  目前,Go 语言标准库中大多数涉及浮点数计算的函数已经以使用 float64 参数为主,因此,建议定义变量类型时使用 float64 类型。

1.4.3 字符串

  字符串可以是任何字符序列,其中的字符可能是数字、字母和字符。下面是一些简单的字符串:

  • cow
  • $%^&*
  • a1234

  几乎所有的编程语言都支持字符串,它们通常包含数字、字母或符号。在 Go 语言中,声明并初始化字符串变量很简单。

var a string = 'foo'

  字符串变量可以为空,这种变量非常适合用来积累其他变量中的数据以及存储临时数据。

var  s string = ""

  创建字符串变量后,可将其与其他数据相加,但不能修改原来的值。下面的代码创建个空字符串,再将字符串 foo 附加到末尾,这在 Go 语言中是合法的。

var s  sting = ""
s += "foo"

  不能对字符串执行数学运算,即便它看起来像个数字。要看对看起来像数字的字符串执行数学运算,必须先将其转换为数字类型。

1.4.4 数组

  数组是几乎所有编程语言都支持的另一种数据类型,这是一种比较复杂的类型,因为它包含一系列元素。例如,要表示乐队的成员,使用字符串数组是不错的选择。声明数组时,必须制定其长度和类型。

var beatles [4]string

  在这个示例中,方括号内的数字表示数组的长度,而紧跟在方括号后的是数组的类型,这里为字符串:

beatles[0] = "John"
beatles[1] = "Paul"
beatles[2] = "Ringo"
beatles[3] = "George"

  数组索引是从 0 开始。在上述变量声明中,指定的数组长度为 4,但访问这个数组的元素时,索引最大为 3.这是因为在所有数组中,索引都从 0 开始。

1.5 检查变量的类型

  有些情况下,需要检查变量的类型,为此可使用标准库中 reflect 包,它让您能够访问变量的底层类型。在大多数情况下,编译器能发现类型不正确的情形,但在调试或必须核实底层类型时,reflect 包将很有用。

程序:使用 reflect 包检查变量类型


package main

import (
	"fmt"
	"reflect"
)

func main() {
	var s string = "string"
	var i int = 10
	var f float32 = 1.2

	fmt.Println(reflect.TypeOf(s))
	fmt.Println(reflect.TypeOf(i))
	fmt.Println(reflect.TypeOf(f))

}

 1.6 类型转换

  将数据从一种类型转换为另一种类型是常见的编程任务,这通常是在从网络或数据库读取数据进行的。Go 标准库提供了良好的类型转换支持。

valueOfTypeB = typeB(valueOfTypeA)

  简单的转换操作:

// 浮点
a := 5.0

//转换为 int 类型
b := int(a)

  Go允许在底层结构相同的两个类型之间互转。例如:

// blue 类型的底层是 int 类型
type blue int

// a的类型为 blue,底层是 int
var a blue = 5

// 将a(blue)转换为 int,b现在是 int 类型
b := int(5)

// 将b(int)转换为 blue,c现在是 blue 类型
c := blue(b)
  • 不是所有数据类型都能转换的,例如字母格式的string类型"abcd"转换为int肯定会失败
  • 低精度转换为高精度时是安全的,高精度的值转换为低精度时会丢失精度。例如int32转换为int16,float32转换为int
  • 这种简单的转换方式不能对int(float)和string进行互转,要跨大类型转换,可以使用strconv包提供的函数

strconv 包提供了一整套类型转换方法,可用于转换为字符串或字符串转换为其他类型。这个包里提供了很多函数,大概分为几类:

  • 字符串转int:Atoi()
  • int转字符串: Itoa()
  • ParseTP类函数将string转换为TP类型:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。因为string转其它类型可能会失败,所以这些函数都有第二个返回值表示是否转换成功
  • FormatTP类函数将其它类型转string:FormatBool()、FormatFloat()、FormatInt()、FormatUint()
  • AppendTP类函数用于将TP转换成字符串后append到一个slice中:AppendBool()、AppendFloat()、AppendInt()、AppendUint()

1.6.1 strconv:string 和 int 转换

  最常见的是字符串和 int 之间的转换。

  1、 int 转换为字符串:Itoa()

// Itoa(): int -> string
println("a" + strconv,Itoa(32))  //a32

  2、string 转换为 int:Atoi()

func Atoi(s string) (int, error)

  由于string可能无法转换为int,所以这个函数有两个返回值:第一个返回值是转换成int的值,第二个返回值判断是否转换成功。

// Atoi(): string -> int
i,_ := strconv.Atoi("3")
println(3 + i)   // 6

// Atoi()转换失败
i,err := strconv.Atoi("a")
if err != nil {
    println("converted failed")
}

1.6.2 strconv:Parse类函数

  Parse类函数用于转换字符串为给定类型的值:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。

  由于字符串转换为其它类型可能会失败,所以这些函数都有两个返回值,第一个返回值保存转换后的值,第二个返回值判断是否转换成功。

b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)
i, err := strconv.ParseInt("-42", 10, 64)
u, err := strconv.ParseUint("42", 10, 64)

  ParseFloat()只能接收float64类型的浮点数。

  ParseInt()和ParseUint()有3个参数:

func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseUint(s string, base int, bitSize int) (uint64, error)

1.6.3 strconv:Format类函数

  将给定类型格式化为string类型:FormatBool()、FormatFloat()、FormatInt()、FormatUint()。

//FormatInt()和FormatUint()有两个参数:
func FormatInt(i int64, base int) string
func FormatUint(i uint64, base int) string

1.6.4 strconv:Append类函数

  AppendTP类函数用于将TP转换成字符串后append到一个slice中:AppendBool()、AppendFloat()、AppendInt()、AppendUint()。
  Append类的函数和Format类的函数工作方式类似,只不过是将转换后的结果追加到一个slice中。

posted @ 2021-08-09 16:34  左扬  阅读(133)  评论(0编辑  收藏  举报
levels of contents