掌握Go语言变量与数据类型

98 阅读17分钟

Go语言,又称为Golang,是谷歌推出的一种静态类型、编译型的开源编程语言。本篇文章将为您介绍Go语言中的变量、常量和基本数据类型。

编译型语言是一种需要通过编译器将源代码转换成机器代码的编程语言。编译器将源代码转化为目标代码,这是一种能够被计算机硬件直接执行的二进制代码。由于在编译时已经将代码转换成机器代码,因此编译型语言的程序在运行时可以直接与计算机硬件交互,因此通常比解释型语言的程序运行速度更快。

Go是一种编译型语言。当你写好Go代码后,需要通过Go编译器将其编译成可执行文件,然后才能在计算机上运行。Go编译器将Go代码编译成本地机器代码,这使得Go程序在运行时可以直接与计算机硬件交互,从而获得更好的性能和可靠性。

静态类型语言是一种编程语言,其特点是在编译时进行类型检查。在静态类型语言中,每个变量都必须在声明时指定其类型,而且在编译时会检查变量的类型是否与其操作相匹配。如果类型不匹配,编译器将会报错,程序无法通过编译。

静态类型语言的优点是可以提前发现类型错误,避免在运行时因类型错误导致的程序崩溃等问题。此外,静态类型语言的编译器可以针对程序的类型信息进行优化,从而提高程序的性能。

Go语言中的变量和常量

在Go语言中,变量是用于存储数据的标识符,而常量是固定值的标识符。Go语言支持显式和隐式声明变量。

1.1 变量的声明和赋值

显式声明变量的方法如下:

var variableName type
//变量声明并赋值
var d int = 4

隐式声明变量的方法如下: 只能在函数内部使用

variableName := value
//变量声明并赋值,省略var,类型推断
//只能在函数内部使用
j := 10
k := 11
l := 12
println(j, k, l)

1.2 常量的声明

声明常量的方法如下:

const constantName type = value
const v int = 22
//常量声明,类型推断
const y = 25
const hh, ii, jj = 34, 35, 36
const (
   kk = 37
   ll = 38
)

Go语言中的基本数据类型

Go语言支持多种基本数据类型,包括数值型、布尔型和字符串等。

数据类型描述默认值大小(字节)
bool布尔类型,表示真或假,取值为 truefalsefalse1
byte字节类型,表示ASCII字符中的一个字节,取值范围为 0 ~ 25501
runeUnicode字符类型,表示Unicode字符,取值范围为 0 ~ 0x10FFFF04
int整数类型,根据机器位数可以是32位或64位,取值范围为 -2147483648 ~ 2147483647-9223372036854775808 ~ 922337203685477580704或8
int88位整数类型,取值范围为 -128 ~ 12701
int1616位整数类型,取值范围为 -32768 ~ 3276702
int3232位整数类型,取值范围为 -2147483648 ~ 214748364704
int6464位整数类型,取值范围为 -9223372036854775808 ~ 922337203685477580708
uint无符号整数类型,根据机器位数可以是32位或64位,取值范围为 0 ~ 42949672950 ~ 1844674407370955161504或8
uint88位无符号整数类型,取值范围为 0 ~ 25501
uint1616位无符号整数类型,取值范围为 0 ~ 6553502
uint3232位无符号整数类型,取值范围为 0 ~ 429496729504
uint6464位无符号整数类型,取值范围为 0 ~ 1844674407370955161508
float32单精度浮点数类型,取值范围为 -3.4E38 ~ 3.4E380.04
float64双精度浮点数类型,取值范围为 -1.7E308 ~ 1.7E3080.08
complex6432位复数类型,由两个float32类型表示实部和虚部。0 + 0i8

Go语言中的复合数据类型

Go语言中的复合数据类型:数组、切片、映射和结构体

数组

数组是一种固定大小、存储相同类型元素的序列。在 Go 语言中,数组是值类型,即当数组被赋值给新变量或作为函数参数传递时,会创建一个数组的副本。数组的长度在声明时确定,并且无法改变。

数组的声明和初始化

在 Go 语言中,可以使用以下方式声明和初始化数组:

var arr [5]int // 声明一个长度为 5 的 int 类型数组,所有元素初始化为 0
arr[0] = 1 // 修改第一个元素的值
fmt.Println(arr) // 输出 [1 0 0 0 0]

arr1 := [5]int{1, 2, 3, 4, 5} // 声明并初始化一个长度为 5 的 int 类型数组
fmt.Println(arr1) // 输出 [1 2 3 4 5]

arr2 := [...]int{1, 2, 3} // 根据元素个数自动推断数组长度,声明并初始化一个 int 类型数组
fmt.Println(arr2) // 输出 [1 2 3]

数组的长度和容量

在 Go 语言中,可以使用内置的 len() 函数获取数组的长度。例如:

arr := [5]int{1, 2, 3, 4, 5}
fmt.Println(len(arr)) // 输出 5

数组的容量指的是数组能够容纳的元素的最大数量。在 Go 语言中,数组的容量等于其长度。因为数组的长度在声明时确定,并且无法改变,所以数组的容量也无法改变。

数组的遍历

可以使用 for range 循环遍历数组中的元素。例如:

arr := [5]int{1, 2, 3, 4, 5}
for i, v := range arr {
    fmt.Println(i, v)
}

数组的比较

在 Go 语言中,可以使用 ==!= 操作符比较两个数组是否相等。两个数组相等,当且仅当它们的长度相等,并且对应位置上的元素也相等。例如:

arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr1 == arr2) // 输出 true

arr3 := [5]int{5, 4, 3, 2, 1}
fmt.Println(arr1 == arr3) // 输出 false

切片

切片是一种动态数组,它可以根据需要自动增长和收缩。切片底层是一个数组,但切片的长度和容量可以动态调整。

切片的声明和初始化

在 Go 语言中,可以使用以下方式声明和初始化切片:

var s []int // 声明一个 int 类型的切片
s = append(s, 1, 2, 3) // 向切片中添加元素
fmt.Println(s) // 输出 [1 2 3]

s1 := []int{1, 2, 3} // 声明并初始化一个 int 类型的切片
fmt.Println(s1) // 输出 [1 2 3]

s2 := make([]int, 3, 5) // 创建一个长度为 3,容量为 5 的 int 类型切片
fmt.Println(s2) // 输出 [0 0 0]
fmt.Println(len(s2)) // 输出 3
fmt.Println(cap(s2)) // 输出 5

切片的长度和容量

在 Go 语言中,可以使用内置的 len() 函数获取切片的长度,使用内置的 cap() 函数获取切片的容量。例如:

s := []int{1, 2, 3}
fmt.Println(len(s)) // 输出 3
fmt.Println(cap(s)) // 输出 3

切片的长度指的是切片中实际存储的元素数量,而容量指的是切片中最多可以存储的元素数量。当向切片中添加元素时,如果元素个数超过了容量,切片的容量将会自动扩展。

切片的遍历

可以使用 for range 循环遍历切片中的元素。例如:

s := []int{1, 2, 3}
for i, v := range s {
    fmt.Println(i, v)
}

切片的添加和删除元素

在 Go 语言中,可以使用内置的 append() 函数向切片中添加元素,也可以使用切片表达式删除切片中的元素。

// 向切片中添加元素
s1 := []int{1, 2, 3}
s1 = append(s1, 4, 5)
fmt.Println(s1) // 输出 [1 2 3 4 5]

// 删除切片中的元素
s2 := []int{1, 2, 3, 4, 5}
s2 = append(s2[:2], s2[3:]...)
fmt.Println(s2) // 输出 [1 2 4 5]

切片的复制

在 Go 语言中,可以使用内置的 copy() 函数将一个切片的元素复制到另一个切片中。例如:

s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1)
fmt.Println(s2) // 输出 [1 2 3]

切片表达式

在 Go 语言中,可以使用切片表达式来获取子切片。切片表达式的语法为 slice[low:high],其中 lowhigh 分别是切片中元素的起始索引和结束索引(不包含结束索引)

在切片表达式中,lowhigh 可以省略其中一个,表示从切片的开头或结尾开始截取。例如:

s1 := []int{1, 2, 3, 4, 5}
fmt.Println(s1[1:3]) // 输出 [2 3]
fmt.Println(s1[:3]) // 输出 [1 2 3]
fmt.Println(s1[3:]) // 输出 [4 5]

切片表达式还可以用来获取包含所有元素的新切片。例如:

s1 := []int{1, 2, 3, 4, 5}
s2 := s1[:]
fmt.Println(s2) // 输出 [1 2 3 4 5]

切片的扩容

在向切片中添加元素时,如果元素个数超过了切片的容量,切片的容量将会自动扩展。切片的扩容策略是,当切片的长度和容量都不足时,将容量翻倍。例如:

s := []int{1, 2, 3}
fmt.Println(cap(s)) // 输出 3

s = append(s, 4, 5)
fmt.Println(cap(s)) // 输出 6

s = append(s, 6, 7, 8, 9)
fmt.Println(cap(s)) // 输出 12

切片与数组的区别

数组和切片都是 Go 语言中常用的数据结构,它们之间有以下区别:

  • 数组的长度在声明时确定,并且无法改变,而切片的长度和容量可以动态调整。
  • 数组是值类型,即当数组被赋值给新变量或作为函数参数传递时,会创建一个数组的副本,而切片是引用类型,即多个变量可以引用同一个切片,它们之间共享底层数组。
  • 在使用 == 操作符比较两个数组是否相等时,当且仅当它们的长度相等,并且对应位置上的元素也相等,它们才被认为是相等的。而对于切片,不能直接使用 == 操作符进行比较,需要使用 reflect.DeepEqual() 函数或自己实现切片元素的比较逻辑。

映射

映射(Map)是一种无序的键值对集合。在 Go 语言中,映射是引用类型,它的零值为 nil,表示空映射。映射的键必须是支持相等运算符(==)的类型,而值可以是任意类型。

映射的声明和初始化

在 Go 语言中,可以使用以下方式声明和初始化映射:

var m map[string]int // 声明一个 string 类型为键、int 类型为值的映射
m = make(map[string]int) // 初始化映射
m["one"] = 1 // 向映射中添加键值对
fmt.Println(m) // 输出 map[one:1]

m1 := map[string]int{"one": 1, "two": 2, "three": 3} // 声明并初始化一个映射
fmt.Println(m1) // 输出 map[one:1 two:2 three:3]

映射的操作

在 Go 语言中,可以使用以下方式对映射进行操作:

添加或修改键值对

可以通过给映射赋值的方式添加或修改键值对。例如:

m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // 输出 map[one:1 two:2]

m["one"] = 3 // 修改键值对
fmt.Println(m) // 输出 map[one:3 two:2]

删除键值对

可以使用 delete() 函数删除映射中的键值对。例如:

m := map[string]int{"one": 1, "two": 2, "three": 3}
delete(m, "two")
fmt.Println(m) // 输出 map[one:1 three:3]

获取键值对

可以使用映射的键来获取对应的值。如果键不存在,将返回值类型的零值。例如:

m := map[string]int{"one": 1, "two": 2, "three": 3}
fmt.Println(m["one"]) // 输出 1
fmt.Println(m["four"]) // 输出 0

可以使用多重赋值的方式获取键值对和是否存在的标志。例如:

m := map[string]int{"one": 1, "two": 2, "three": 3}
v, ok := m["four"]
fmt.Println(v, ok) // 输出 0 false

遍历映射

可以使用 for range 循环遍历映射中的键值对。例如:

m := map[string]int{"one": 1, "two": 2, "three": 3}
for k, v := range m {
    fmt.Println(k, v)
}

映射与切片、数组的区别

在 Go 语言中,映射、切片和数组都是常用的数据结构,它们之间有以下区别:

  • 数组和切片都是有序的元素集合,而映射是无序的键值对集合。
  • 数组和切片的元素可以是任意类型,而映射的键必须是支持相等运算符(==)的类型,值可以是任意类型。
  • 数组是值类型,即当数组被赋值给新变量或作为函数参数传递时,会创建一个数组的副本,而切片和映射都是引用类型,即多个变量可以引用同一个切片或映射,它们之间共享底层数据结构。

总结

映射是一种无序的键值对集合,在 Go 语言中是引用类型,其零值为 nil。可以使用 make() 函数创建一个非空的映射,并可以通过给映射赋值的方式添加或修改键值对,使用 delete() 函数删除键值对,使用映射的键获取对应的值。可以使用 for range 循环遍历映射中的键值对。在实际开发中,需要根据实际需求选择合适的数据结构,并掌握它们的常见用法和方法,以提高编码效率和程序性能。

结构体

结构体(Struct)是一种自定义数据类型,它由一组不同类型的字段组成,每个字段可以是基本类型、结构体类型或者数组类型。在 Go 语言中,可以使用结构体来定义复杂的数据结构,以提高代码的可读性和可维护性。

结构体的声明和初始化

在 Go 语言中,可以使用以下方式声明和初始化结构体:

// 声明一个结构体类型
type Person struct {
    name string
    age  int
}

// 初始化一个结构体变量
p1 := Person{name: "Tom", age: 18}
fmt.Println(p1) // 输出 {Tom 18}

// 声明并初始化一个结构体变量
p2 := Person{"Jerry", 20}
fmt.Println(p2) // 输出 {Jerry 20}

结构体的字段访问

在 Go 语言中,可以使用结构体变量的字段名来访问结构体的字段。例如:

p := Person{name: "Tom", age: 18}
fmt.Println(p.name) // 输出 Tom
fmt.Println(p.age) // 输出 18

结构体的嵌套

在 Go 语言中,可以在结构体中嵌套其他的结构体。例如:

type Address struct {
    Province string
    City     string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

p := Person{
    Name: "Tom",
    Age:  18,
    Address: Address{
        Province: "Guangdong",
        City:     "Shenzhen",
    },
}
fmt.Println(p) // 输出 {Tom 18 {Guangdong Shenzhen}}

结构体的匿名字段

在 Go 语言中,可以使用结构体的匿名字段来简化代码。匿名字段指的是没有字段名的字段,其类型可以是基本类型、结构体类型或者数组类型。例如:

type Person struct {
    string
    int
}

p := Person{"Tom", 18}
fmt.Println(p.string) // 输出 Tom
fmt.Println(p.int) // 输出 18

结构体的方法

在 Go 语言中,可以给结构体定义方法。方法是一种特殊的函数,它属于结构体类型,可以访问结构体的字段。在方法的定义中,可以使用接收者(Receiver)来指定方法所属的结构体类型。例如:

type Person struct {
    name string
    age  int
}

// 定义一个方法,该方法属于 Person 类型
func (p Person) sayHello() {
    fmt.Printf("Hello, my name is %s, I'm %d years old.\n", p.name, p.age)
}

p := Person{"Tom", 18}
p.sayHello() // 输出 Hello, my name is Tom, I'm 18 years old.

好的,以下是关于结构体的详细介绍:

结构体

结构体(Struct)是一种自定义数据类型,它由一组不同类型的字段组成,每个字段可以是基本类型、结构体类型或者数组类型。在 Go 语言中,可以使用结构体来定义复杂的数据结构,以提高代码的可读性和可维护性。

结构体的声明和初始化

在 Go 语言中,可以使用以下方式声明和初始化结构体:

goCopy code
// 声明一个结构体类型
type Person struct {
    name string
    age  int
}

// 初始化一个结构体变量
p1 := Person{name: "Tom", age: 18}
fmt.Println(p1) // 输出 {Tom 18}

// 声明并初始化一个结构体变量
p2 := Person{"Jerry", 20}
fmt.Println(p2) // 输出 {Jerry 20}

结构体的字段访问

在 Go 语言中,可以使用结构体变量的字段名来访问结构体的字段。例如:

goCopy code
p := Person{name: "Tom", age: 18}
fmt.Println(p.name) // 输出 Tom
fmt.Println(p.age) // 输出 18

结构体的嵌套

在 Go 语言中,可以在结构体中嵌套其他的结构体。例如:

goCopy code
type Address struct {
    Province string
    City     string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

p := Person{
    Name: "Tom",
    Age:  18,
    Address: Address{
        Province: "Guangdong",
        City:     "Shenzhen",
    },
}
fmt.Println(p) // 输出 {Tom 18 {Guangdong Shenzhen}}

结构体的匿名字段

在 Go 语言中,可以使用结构体的匿名字段来简化代码。匿名字段指的是没有字段名的字段,其类型可以是基本类型、结构体类型或者数组类型。例如:

goCopy code
type Person struct {
    string
    int
}

p := Person{"Tom", 18}
fmt.Println(p.string) // 输出 Tom
fmt.Println(p.int) // 输出 18

结构体的方法

在 Go 语言中,可以给结构体定义方法。方法是一种特殊的函数,它属于结构体类型,可以访问结构体的字段。在方法的定义中,可以使用接收者(Receiver)来指定方法所属的结构体类型。例如:

goCopy code
type Person struct {
    name string
    age  int
}

// 定义一个方法,该方法属于 Person 类型
func (p Person) sayHello() {
    fmt.Printf("Hello, my name is %s, I'm %d years old.\n", p.name, p.age)
}

p := Person{"Tom", 18}
p.sayHello() // 输出 Hello, my name is Tom, I'm 18 years old.

在方法定义中,接收者的类型可以是值类型或指针类型。如果接收者是值类型,则在方法中对结构体的修改不会影响原来的结构体变量;如果接收者是指针类型,则在方法中对结构体的修改会影响原来的结构体变量。例如:

type Person struct {
    name string
    age  int
}

// 定义一个方法,该方法属于 Person 类型的指针
func (p *Person) growUp() {
    p.age++
}

p := &Person{"Tom", 18}
p.growUp()
fmt.Println(p.age) // 输出 19

结构体与指针的区别

在 Go 语言中,结构体和指针都是常用的数据类型,它们之间有以下区别:

  • 结构体是值类型,即当结构体变量被赋值给新变量或作为函数参数传递时,会创建一个结构体的副本,而指针是引用类型,即多个变量可以指向同一个内存地址。
  • 当访问结构体的字段时,可以使用结构体变量的点号运算符(.)或指针变量的箭头运算符(->)来访问。例如:
type Person struct {
   name string
   age  int
}

p := Person{"Tom", 18}
q := &p
fmt.Println(p.name) // 输出 Tom
fmt.Println(q->name) // 输出 Tom
  • 在使用 new() 函数或 & 运算符创建指向结构体的指针时,可以使用结构体字面量直接初始化结构体的字段,也可以使用 . 运算符来访问结构体的字段。例如:
type Person struct {
    name string
    age  int
}

p := &Person{name: "Tom", age: 18}
q := new(Person)
q.name = "Jerry"
q.age = 20

总结

结构体是一种自定义的数据类型,由一组不同类型的字段组成。可以使用结构体来定义复杂的数据结构,以提高代码的可读性和可维护性。可以使用结构体变量的字段名来访问结构体的字段,使用结构体的匿名字段来简化代码。可以给结构体定义方法,方法是一种特殊的函数,它属于结构体类型,可以访问结构体的字段。在实际开发中,需要根据实际需求选择合适的数据类型,并掌握它们的常见用法和方法,以提高编码效率和程序性能。