Go 基础教程

original icon
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.knowledgedict.com/tutorial/go-data-types.html

Go 语言数据类型


在 Go 编程语言中,数据类型用于声明函数和变量。Go 是一个静态类型(statically typed)语言,意味着一旦定义变量类型,该变量只能存储该类型的数据。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。

数据分类

Go 语言将数据分成如下四大类(categories):

  1. 基本类型(basic type):其包括数字类型(number)、布尔类型(boolean)和字符串类型(string);
  2. 聚合类型(aggregate type):该分类下包括数组类型(array)和结构体(struct);
  3. 引用类型(reference type):指针(pointer)、切片(slice)、字典(map)、函数(function)和通道(channel)都属于该分类;
  4. 接口类型(interface type):Go 语言中接口(interface)是一个类型,是一个抽象的类型。

基本类型

Go 语言基本类型有数字类型、布尔类型和字符串类型。

数字类型

在 Go 语言中,数字类型又细分为整数类型、浮点类型和复数类型。

有符号和无符号整数都有四种不同的大小,如下表所示。有符号的整数用 int 表示,无符号整数用 uint 表示。

类型 描述
int8 8 位有符号整数
int16 16 位有符号整数
int32 32 位有符号整数
int64 64 位有符号整数
uint8 8 位无符号整数
uint16 16 位无符号整数
uint32 32 位无符号整数
uint64 64 位无符号整数
int int 和 uint 都包含相同的大小,32 位或 64 位
uint int 和 uint 都包含相同的大小,32 位或 64 位
rune 它是 int32 的同义词
byte 它是 uint8 的同义词
uintptr 它是无符号整数类型,它的宽度没有定义,但它可以保存指针值的所有位

示例如下:

package main

import "fmt"

func main() {

	// Using 8-bit unsigned int
	var X uint8 = 225
	fmt.Println(X, X-3)

	// Using 16-bit signed int
	var Y int16 = 32767
	fmt.Println(Y+2, Y-2)
}

输出如下:

225 222
-32767 32765

浮点类型分为如下两个类型:

类型 描述
float32 32 位 IEEE 754 浮点数类型
float64 64 位 IEEE 754 浮点数类型

示例如下:

// Go program to illustrate
// the use of floating-point
// numbers
package main

import "fmt"

func main() {
	a := 20.45
	b := 34.89

	// Subtraction of two
	// floating-point number
	c := b - a

	// Display the result
	fmt.Printf("Result is: %f", c)

	// Display the type of c variable
	fmt.Printf("\nThe type of c is : %T", c)
}

输出如下:

Result is: 14.440000
The type of c is : float64

复数类型分为两部分,如下表所示。float32 和 float64 也是这些复数的一部分。内置函数从其虚部和实部创建复数,并且内置虚部和实部提取这些部分。

类型 描述
complex64 包含 float32 作为实部和虚部的复数
complex128 包含 float64 作为实部和虚部的复数

示例如下:

// Go program to illustrate
// the use of complex numbers
package main

import "fmt"

func main() {

	var a complex128 = complex(6, 2)
	var b complex64 = complex(9, 2)
	fmt.Println(a)
	fmt.Println(b)

	// Display the type
	fmt.Printf("The type of a is %T and the type of b is %T", a, b)
}

输出如下:

(6+2i)
(9+2i)
The type of a is complex128 and the type of b is complex64

布尔类型

boolean 数据类型只有 true 和 false 两个值。boolean 类型的值不会隐式或显式转换为任何其他类型。

示例如下:

// Go program to illustrate
// the use of booleans
package main

import "fmt"

func main() {

	// variables
	str1 := "Knowledgedict"
	str2 := "knowledgeDict"
	str3 := "Knowledgedict"
	result1 := str1 == str2
	result2 := str1 == str3

	// Display the result
	fmt.Println(result1)
	fmt.Println(result2)

	// Display the type of
	// result1 and result2
	fmt.Printf("The type of result1 is %T and the type of result2 is %T", result1, result2)

}

输出如下:

false
true
The type of result1 is bool and the type of result2 is bool

字符串类型

string 数据类型用于存储字符序列(文本)。字符串值必须用双引号括起来。

字符串是不可变的字节序列,这意味着一旦创建了字符串,就无法更改该字符串。字符串可以包含任意数据。

代码示例如下:

// Go program to illustrate
// the use of strings
package main

import "fmt"

func main() {

	// str variable which stores strings
	str := "knowledgedict"

	// Display the length of the string
	fmt.Printf("Length of the string is:%d", len(str))

	// Display the string
	fmt.Printf("\nString is: %s", str)

	// Display the type of str variable
	fmt.Printf("\nType of str is: %T", str)
}

执行结果如下:

Length of the string is:13
String is: knowledgedict
Type of str is: string

聚合类型

Go 语言中聚合类型包括数组类型和结构体类型。

数组类型

Go 编程语言中的数组与其他编程语言非常相似。我们需要存储一组相同类型的数据,这种类型的集合使用数组存储在程序中。

数组是一个固定长度的序列,用于在内存中存储同质元素。由于它们的固定长度数组不像 Go 语言中的 Slice 那样流行。在数组中,您可以在其中存储零个或多个零个元素。

初始化语法如下:

array_name := [length]Type{item1, item2, item3,...itemN}

示例如下:

// Go program to illustrate how to create
// an array using shorthand declaration
// and accessing the elements of the
// array using for loop
package main

import "fmt"

func main() {

	// Shorthand declaration of array
	arr := [4]string{"knowledge", "dict", "188", "knowledgedict"}

	// Accessing the elements of
	// the array Using for loop
	fmt.Println("Elements of the array:")

	for i := 0; i < 3; i++ {
		fmt.Println(arr[i])
	}

}

输出如下:

Elements of the array:
knowledge
dict
188

结构体类型

Go 语言中的结构体是用户定义的类型,它允许将不同类型的元素封装成一个类型。任何具有一组属性/字段的现实世界实体都可以表示为结构体。这个概念通常与面向对象编程中的类进行比较。它可以称为不支持继承,但支持组合的轻量级类。

例如,Address 具有 name、city 和 Pincode。将这几个属性分组到一个结构体中是有意义的,如下所示:

type Address struct {
      name string 
      city string
      Pincode int
}

示例如下:

// Golang program to show how to
// declare and define the struct

package main

import "fmt"

// Defining a struct type
type Address struct {
	Name    string
	city    string
	Pincode int
}

func main() {

	// Declaring a variable of a `struct` type
	// All the struct fields are initialized
	// with their zero value
	var a Address
	fmt.Println(a)

	// Declaring and initializing a
	// struct using a struct literal
	a1 := Address{"Akshay", "Dehradun", 3623572}

	fmt.Println("Address1: ", a1)

	// Naming fields while
	// initializing a struct
	a2 := Address{Name: "Anikaa", city: "Ballia",
		Pincode: 277001}

	fmt.Println("Address2: ", a2)

	// Uninitialized fields are set to
	// their corresponding zero-value
	a3 := Address{Name: "Delhi"}
	fmt.Println("Address3: ", a3)
}

输出如下:

{  0}
Address1:  {Akshay Dehradun 3623572}
Address2:  {Anikaa Ballia 277001}
Address3:  {Delhi  0}

引用类型

Go 语言中的引用类型(reference type)指的是指针(pointer)、切片(slice)、字典(map)、函数(function)和通道(channel)类型。

指针类型

Go 编程语言中的指针是一个变量,用于存储另一个变量的内存地址。Go 语言中的指针也称为特殊变量。这些变量用于在系统中的特定内存地址存储一些数据。内存地址总是以十六进制格式(以 0x 开头,如 0xFFAAF 等)。

变量是存储实际数据的内存位置的名称。要访问存储的数据,我们需要该特定内存位置的地址。手动记住所有内存地址(十六进制格式)是一种开销,这就是我们使用变量来存储数据的原因,而变量只需使用它们的名称就可以访问。

Go 语言还允许使用文字表达式将十六进制数保存到变量中,即从 0x 开始的数字是十六进制数。

示例如下:

// Golang program to demonstrate the variables
// storing the hexadecimal values
package main

import "fmt"

func main() {

	// storing the hexadecimal
	// values in variables
	x := 0xFF
	y := 0x9C

	// Displaying the values
	fmt.Printf("Type of variable x is %T\n", x)
	fmt.Printf("Value of x in hexadecimal is %X\n", x)
	fmt.Printf("Value of x in decimal is %v\n", x)

	fmt.Printf("Type of variable y is %T\n", y)
	fmt.Printf("Value of y in hexadecimal is %X\n", y)
	fmt.Printf("Value of y in decimal is %v\n", y)

}

输出结果如下:

Type of variable x is int
Value of x in hexadecimal is FF
Value of x in decimal is 255
Type of variable y is int
Value of y in hexadecimal is 9C
Value of y in decimal is 156

切片类型

在 Go 语言中,切片比数组更强大、更灵活、更方便,是一种轻量级的数据结构。Slice 是一个可变长度的序列,用于存储相似类型的元素,不允许在同一个 slice 中存储不同类型的元素。它就像一个具有索引值和长度的数组,但是切片的大小被调整大小,它们不像数组那样是固定大小的。在内部,切片和数组是相互连接的,切片是对底层数组的引用。允许在切片中存储重复的元素。切片中的第一个索引位置始终为 0,最后一个为长度(切片长度 -1)。

切片就像数组一样声明,但它不包含切片的大小。所以它可以根据需要动态变化。

具体语法如下:

[]T

或者

[]T{}

或者

[]T{value1, value2, value3, ...value n}

其中 T 指的是元素类型。

示例如下:

// Golang program to illustrate
// the working of the slice components
package main

import "fmt"

func main() {

	// Creating an array
	arr := [7]string{"This", "is", "the", "tutorial",
		"of", "Go", "language"}

	// Display array
	fmt.Println("Array:", arr)

	// Creating a slice
	myslice := arr[1:6]

	// Display slice
	fmt.Println("Slice:", myslice)

	// Display length of the slice
	fmt.Printf("Length of the slice: %d", len(myslice))

	// Display the capacity of the slice
	fmt.Printf("\nCapacity of the slice: %d", cap(myslice))
}

输出如下:

Array: [This is the tutorial of Go language]
Slice: [is the tutorial of Go]
Length of the slice: 5
Capacity of the slice: 6

字典类型

在 Go 语言中,字典类型的官方称谓是 map,它是哈希表(Hash Table)的一个实现。

如果一个字典类型的键的类型为 K,且元素的类型为 T,那么用于表示这个字典类型的类型字面量:

map[K]T

字典类型声明中的元素类型可以是任意一个有效的 Go 语言数据类型(除了函数类型、字典类型或切片类型)。键的类型必须是可比较的。如果字典类型的键类型是接口类型,那么就要求在程序运行期间,该类型的字典值中的每一个键值的动态类型都必须是可比较的。否则在进行相应操作的时候会引发运行时异常。如下:

map[int]string  //合法
map[string]struct{name, department string}  //合法
map[string]interface{}  //合法
map[[]int]string  //不合法
map[map[int]string]string  //不合法

初始化示例如下:

package main

import "fmt"

func main() {

	mp := map[string]bool{"knowledge": true, "dict": false}

	// Display map
	fmt.Println("map:", mp)
}

输出如下:

map: map[dict:false knowledge:true]

函数

函数通常是程序中的代码块或语句,使用户能够重用相同的代码,最终节省内存的过度使用,节省时间,更重要的是,提供更好的代码可读性。所以基本上,函数是执行某些特定任务并将结果返回给调用者的语句的集合。一个函数也可以执行一些特定的任务而不返回任何东西。

语法:

func function_name(Parameter-list)(Return_type){
    // function body.....
}

函数示例如下:

// Go program to illustrate the
// use of function
package main

import "fmt"

// area() is used to find the
// area of the rectangle
// area() function two parameters,
// i.e, length and width
func area(length, width int) int {

	Ar := length * width
	return Ar
}

// Main function
func main() {

	// Display the area of the rectangle
	// with method calling
	fmt.Printf("Area of rectangle is : %d", area(12, 10))
}

输出结果:

Area of rectangle is : 120

通道

Go 语言中的通道(channel)是一种特殊的类型。

在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。

通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

示例如下:

package main

import "fmt"

func main() {

	//演示一下管道的使用
	//1. 创建一个可以存放3个int类型的管道
	var intChan chan int
	intChan = make(chan int, 3)

	//2. 看看intChan是什么
	fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)

	//3. 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 50
	// //如果从channel取出数据后,可以继续放入
	<-intChan
	intChan <- 98 //注意点, 当我们给管写入数据时,不能超过其容量

	//4. 看看管道的长度和cap(容量)
	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 3, 3

	//5. 从管道中读取数据

	var num2 int
	num2 = <-intChan
	fmt.Println("num2=", num2)
	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 2, 3

	//6. 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock

	num3 := <-intChan
	num4 := <-intChan

	//num5 := <-intChan

	fmt.Println("num3=", num3, "num4=", num4 /*, "num5=", num5*/)

}

输出结果如下:

intChan 的值=0xc000104000 intChan本身的地址=0xc000012028
channel len= 3 cap=3 
num2= 211
channel len= 2 cap=3 
num3= 50 num4= 98

接口类型

Go 语言中的接口是一种类型,类似于 Python 中的抽象基类。Go 语言中使用接口来体现多态,是 duck-type 的一种体现。

接口类型描述了方法的集合,实现此接口即这些方法的具体类型是这个接口的实例。

io.Writer 提供了所有可以写入 bytes 的抽象,如文件、内存缓冲区、网络连接、HTTP 客户端、压缩工具、hash 等。

io.Reader 代表了任意可以读取 bytes 的类型。

Closer 是任意可以关闭的值,如文件或网络连接。

语法示例如下:

type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

接口名在单词后面一般都需添加 er 的后缀,代表这是一个接口。

当接口名与方法名都是大写时,代表该接口与方法均可被外部包进行访问。

参数列表以及返回值列表参数变量名可以省略。

示例如下:

package main

import "fmt"

type animal interface {
	move()
	roar()
}

func sapo(a animal) {
	a.move()
	a.roar()
}

type dog struct {
	name string
}

func (d dog) move() {
	fmt.Printf("%s在移动\n", d.name)
}

func (d dog) roar() {
	fmt.Printf("%s在吼叫\n", d.name)
}

type wolf struct {
	name string
}

func (w wolf) move() {
	fmt.Printf("%s在移动\n", w.name)
}

func (w wolf) roar() {
	fmt.Printf("%s在吼叫\n", w.name)
}

func main() {
	d1 := dog{
		name: "大黄",
	}
	w1 := wolf{
		name: "灰太狼",
	}
	sapo(d1) // 大黄调用动物的撒泼方法,由于会动会叫就是动物,所以大黄有两种类型,一种是动物,一种是狗
	sapo(w1)
}

输出如下:

大黄在移动
大黄在吼叫
灰太狼在移动
灰太狼在吼叫