【Golang】go语言之基础介绍

变量的定义和赋值

定义(声明)一个变量

1
2
3
4
5
6
7
8
9
var a, b int	// 声明同类型
var a, b int = 10, 20 // 仅同类型 blockchain making transactions safer and faster

// 声明不同类型
var (
a int
b string
c, d int
)

变量初始化

三种方式:完整式、类型推导、简短式

常量

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
// const 常量名 类型 = 值
const a int = 100
const b = 200 // 类型推导
fmt.Println(b)
// 方式2
const (
n1 = 100
n2 = 200 // n2
n3 = 300
)

}

命名规范

定义规范

  • Go语言中的变量名、常量名、函数名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:
1
2
3
1 由字符,下划线,数字组成,但是不能以数字开头
2 大写字母和小写字母是不同的:Name和name是两个不同的变量
3 关键字不能用做命名;保留字都不建议用作变量名

命名风格

  • Go语言程序员推荐使用 驼峰式 命名,当名字由几个单词组成时优先使用大小写分隔,而不是优先用下划线分隔。
  • go文件的名字,建议用下划线的方式命名。
  • 名字的长度没有逻辑限制,但是Go语言的风格是尽量使用短小有意义的名字

关键字

  • Go语言中关键字有25个;关键字不能用于自定义名字,只能在特定语法结构中使用。
1
2
3
4
5
break      default       func     interface   select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

保留字

  • go语言中有37个保留字,主要对应内建的常量、类型和函数
1
2
3
4
5
6
7
8
9
10
内建常量: true false iota nil

内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error

内建函数: make len cap new append copy close delete
complex real imag
panic recover

数据类型

基础数据类型

基本数据类型有三类:数字、字符串、布尔

  • 数字类型
    • 整型、浮点数、复数
    • 不同类型的数字不能做运算
  • 整型分为两个大类:
    • 按长度分为:int int8、int16、int32、int64
    • 无符号整型:uint uint8、uint16、uint32、uint64
    • 操作系统不一样,int和uint表示的范围不一样
类型 描述
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)

浮点数分为两个大类:

  • float32: float32 的浮点数的最大范围约为 3.4e38,可以使用常量定义:math.MaxFloat32
  • float64: float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64
  • 默认定义的浮点数是float64
  • 打印浮点数时,可以使用fmt包配合动词%f
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
"math"
)

func main() {
s1 := 1.34
fmt.Printf("%T\n", s1) // float64
fmt.Println(math.MaxFloat32)
fmt.Printf("%.2f\n", math.Pi)
}
  • 复数分为两个大类:
    • complex64和complex128
    • 复数有实部和虚部
1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
var c1 complex64
var c2 complex128
c1 = 3 + 5i
c2 = 4 + 8i
fmt.Printf("c1:%v, type: %T\n", c1, c1) // c1:(3+5i), type: complex64
fmt.Printf("c2:%v, type: %T\n", c2, c2) // c2:(4+8i), type: complex128
  • 布尔值

    • 布尔型的值只可以是常量 true 或者 false
    • 布尔类型变量的零值为false
    • 布尔型不能参与数字运算,也不能与其他类型相互转换
  • 字符串

    • 字符串就是一串固定长度的字符连接起来的字符序列,由双引号包裹
    • 字符串不可修改
    • 字符串的零值是空字符串””
    • Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本
    • 多行字符串的实现有两种方式:转译符号\n、反引号(键盘上Tab键上面那个键)
    • 反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
1
2
3
4
5
6
7
s1 := "hello world"
s2 := "你好 世界"

s3 := `第一行
第二行
第三行
`
  • 字符串本质是由字符组成,

    • 字符由单引号包裹

    • 字符有两种类型:byterune

    • 字节byte用来处理ascii码表中的字符

    • 符文rune来处理其他字符,如汉子。日文等

    • 字符存储的本质所以数字:byte等于int8rune等于int32

类型转换

  • Go语言中没有隐式类型转换,类型转换都是显式的强制转换。
  • 强制类型转换只能在两个类型之间支持相互转换的时候使用。
  • 注意:浮点数转整型时,小数点后的数据会丢失
1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
n1 := 10
n2 := 1.2
n3 := float64(n1) + n2 // 强制将int -> float64,强制转换的方式 T(表达式)
fmt.Println(n3)
}

容器型数据结构

代表:数组(Array)、切片(Slice)、映射(Map)

数组Array

  • 数组是一个由固定长度相同类型元素组成的序列。
  • 数组一旦确定,数组的长度就不能再改变。
  • 类似python的元组

声明数组

1
2
3
4
5
var 数组名 [长度]元素类型
// var是关键字
// 数组名和普通的变量名遵循相同的名分规范和风格
// [],内的数字表示该数组可以存放元素的个数,及数组的长度
// 元素类型,数组内存放元素的类型,一个数组内元素的类型必须一致

数组的零值

  • 数组是值类型,所以数组的零值是对应元素零值的集合
1
2
3
var nums [3]int

fmt.Println(nums) // 输出: [0 0 0]

数组的类型

  • 数组的类型:[数组长度元素类型
  • 数组的长度是类型的一部分,长度不同的两个数组,类型不一样
  • 打印数组类型:fmt.Printf("%T", nums)

数组的长度

  • 数组长度:就是数组内元素的个数
  • 查看数组长度:内置函数len
1
2
var nums = [3]int{1, 2, 3}		// 长度为3的数组
fmt.Println(len(nums)) // 输出3

数组的取值方式

  • 通过索引访问数组中的每个元素
  • 索引从数组0开始,到数组长度减1位置
1
2
3
4
5
var nums [5]int{1,2,3,4,5}
nums[0] // 数组第一个元素
nums[1] // 数组第二个元素
nums[4] // 数组最后一个元素
nums[5] // 报错:索引值越界

数组赋值初始化

  • 类比变量的赋值和初始化
1
2
var nums = [3]int{1, 2, 3}		// 类型推导
nums := [3]int{1, 2, 3} // 简短式
  • 指定位置赋值
1
nums := [5]int{1:10, 3: 30}
  • 自动赋值
1
nums := [...]int{1, 2, 3, 4}

数组长度

  • 通过内置函数:len

遍历数组

  • 使用for循环
1
2
3
for i:=0; i<len(nums); i++{
fmt.Println(nums[i])
}
  • 使用range语句
1
2
3
4
for index, value := range nums{
fmt.Print(index, value)
}
// index是索引,value是元素值; 不想要的可以通过 _ 接收

切片Slice

  • 数组的长度是固定的,不支持扩容,使用起来不方便
  • 数组是值类型,当参数传给函数时值拷贝,无法通过函数修改数组元素

切片概念:

  • 切片是一组相同类型元素的的序列,可以修改长度,使用灵活,自动扩容。
  • 切片是对数组的一个连续片段的引用,所以切片是一个引用类型。

声明切片

  • 基于数组声明一个切片
1
2
3
4
nums := [5]int{1,2,3,4,5}

nums_slice := nums[1:3] // 左闭右开区间,
fmt.Println(nums_slice) // [2,3]
  • 其实位置和结束位置都不能为负数,都可以省略

切片的类型[]元素类型

三大特征

  • 切片内部结构包含:地址大小容量
  • 地址:切片底层是对数组的封装,地址就是底层数组的指针
1
2
3
4
5
nums := [...]int{1,2,3,4,5}
numsSlice := nums[:]

fmt.Printf("%p\n", &nums) // 0xc00000c2d0
fmt.Printf("%p\n", numsSlice) // 0xc00000c2d0
  • 大小:切片内元素的数量,内置方法len
  • 容量:切片最多可以放多少个元素,内置方法cap

切片的零值 nil

切片扩容

  • 使用内置函数给切片添加元素:append
1
2
3
4
b := [10]{}[:]
a = append(a, 1)
a = append(a, 2)
a = append(a, b...) // 将切片b内所有元素追加给a
  • 当元素个数达到切片容量时,切片将自动扩容
  • 扩容的规律:按容量的2倍进行扩展
1
2
3
4
5
var numbers []int
for i := 0; i < 10; i++ {
nums = append(nums, i)
fmt.Printf("len: %d cap: %d pointer: %p\n", len(nums), cap(nums), nums)
}
  • 切片每一次扩容,底层关联的数组都会随之切换。
  • 切片扩容的本质,是关联底层数组的切换。
1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
a := [2]int{}
b := a[:]
fmt.Printf("%p, %p\n", &a, b) // 0xc00000a0c0, 0xc00000a0c0
b = append(b, 1) // 达到b的容量,发生扩容
fmt.Printf("%p, %p\n", &a, b) // 0xc00000a0c0, 0xc0000101e0
}

创建切片的四种方式

基于数组

  • 基于数组切片一个切片
1
2
nums := [3]int{1,2,3}
numSlice := nums[1:3]

基于切片

  • 与基于数组创建类似,在切片上再切出一个切片
1
2
3
nums := [3]int{1,2,3}
numSlice := nums[:]
numSlice2 := numSlice[1:]

直接声明切片

  • 直接使用切片的类型声明切片:var sliceName []元素类型
  • 如果仅声明一个切片,它是没有分配内存的。
1
2
3
4
5
6
7
8
9
var strSlice []string			// 声明字符串切片
var numSlice []int // 声明整型切片
var numSlice2 = []int{} // 声明并初始化

fmt.Println(strSlice, numSlice, numSlice2) // [] [] []
fmt.Println(len(strSlice), len(numSlice), len(numSlice2)) // 0,0,0
fmt.Println(strSlice == nil) // true
fmt.Println(numSlice == nil) // true
fmt.Println(numSlice2 == nil) // false

make函数构造切片

1
2
sliceName := make([]T, size, cap)		// 格式
sliceTest := make([]string,10)
  • 使用内置函数make构造切片
  • 创建的切片已经分配了内存,可以直接使用
  • T是切片元素类型
  • size是切片的长度
  • cap是提前分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。如果不指定则使用默认值。

复制切片

  • 复制切片,也就拷贝切片,使用内置函数:copy
  • 将一个切片的元素对应位置拷贝给另一个切片,复制的元素个数以长度较小的切片为准。
1
copy(a, b)		// 将切片b的元素拷贝给切片a

删除切片

  • go没有提供专门删除切片内元素的方法,但可以利用切片的特性删除元素。
  • 方式:移动切片的位置,append,copy等
1
2
3
4
5
6
7
8
9
10
11
// 方式1:
a = a[1:] // 删除切片a中的第一个元素
a = a[5:] // 删除切片a中前个元素

// append
a = append(a[:0], a[1:]) // 删除切片a中的第一个元素
a = append(a[:0], a[5:]) // 删除切片a中前个元素

// copy
a = a[:copy(a, a[1:])] // 删除切片a中的第一个元素
a = a[:copy(a, a[5:])] // 删除切片a中前个元素

切片做函数的参数

切片是引用类型

  • 切片因为没有长度限制,所以方便做函数参数。

  • 切片做函数参数是引用传递,修改形参将影响实参。

  • 注意:当切片发生扩容时,则对切片做的修改就不再影响原底层数组。

映射map

  • Go语言中 map(又叫映射)是一种特殊的数据类型:一种键(key)值(value)对组成的无序集合.
  • map按照key可以快速查找到value
  • key是字符串或者数字,value可以是任意数据类型,在一个map中通常key和value的类型固定
  • map支持扩容
  • map是引用类型

声明map:

1
var mapName map[keyType]valueType
  • 这种方式声明的map是没有长度,未初始化的map零值是nil

赋值map

1
2
3
map1 = map[string]int{"class1":10, "class2":50}

map1["class3"] = 80

长度和容量

  • 获取长度:len

  • 获取容量:cap

  • 达到最大容量时将自动扩容,一般会预扩容,避免动态扩容时的性能损耗。

make创建map

  • 使用make创建map,获取一个map对象
1
map1 := make(map[string]int, 10)	// 预扩容为10

map的长度和容量

内置函数:lencap

拿键取值

1
fmt.Println(map1["cleas"])

增加键值对

1
2
3
map1 := make(map[string]int, 10)

map1["class1"] = 50 // 增加键值对

修改键值对

1
map1["class1"] = 59		// 修改键值对

删除键值对

  • 使用内置方法:delete
1
delete(map1, "class1")		// 键不存在时,不操作,不报错

清空map

  • 没有专门的清空map的方法:清空就是重新make一个
1
2
3
4
5
a := make(map[string]int, 10)
a["class1"] = 50
a["class2"] = 60

a = make(map[string]int, 10 // 清空map

map是引用类型,做函数参数修改形参将影响实参

new和make的区别

相同点

  • new 和 make 是两个内置函数,都是用来创建并分配类型的内存。

不同点

  • new一般用在值类型,make用在引用类型。
  • new只接受一个参数:类型,返回该类型的指针,同时分配的内存会置为该类型的零值;
  • make只用于 chan、map 以及 slice 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。
1
2
3
4
5
6
7
// new
var a []int // a == nil
b := new([]int) // b != nil

// make
var a map[string]int // a == nil
b := make(map[string]int) // b != nil

重点

  • new 只分配内存,而 make 只能用于 slice、map 和 channel 的初始化。

【Golang】go语言之基础介绍
http://example.com/2023/12/06/801go语言之基础介绍/
作者
Wangxiaowang
发布于
2023年12月6日
许可协议