当前位置:   article > 正文

Golang教程一(环境搭建,变量,数据类型,数组切片map)

Golang教程一(环境搭建,变量,数据类型,数组切片map)

目录

一、环境搭建

1.windows安装

2.linux安装 

3.开发工具

二、变量定义与输入输出

1.变量定义

2.全局变量与局部变量

3.定义多个变量

4.常量定义

5.命名规范

6.输出

7.输入 

三、基本数据类型

1.整数型

2.浮点型

3.字符型

4.字符串类型

转义字符

多行字符串

5.布尔类型

四、数组、切片、map

1.数组

 2.切片

make函数

切片面试题 

3.Map

map面试题 


一、环境搭建

官网

https://go.dev/dl/

访问不了的就访问中文网就好了

go安装包下载

https://studygolang.com/dl

安装指定版本的安装包就好了

1.windows安装

选择 xxx.windows-amd64.msi

  • 将go的对应bin目录设置为环境变量,这一步是方便可以在命令行里面直接使用go命令
  • 将go的第三方bin目录设置为环境变量,一般是在用户目录下,这一步是为了以后使用go install安装的第三方可执行文件可以直接使用

2.linux安装 

选择 xxx.linux-amd64.tar.gz

该站点比较快:All releases - The Go Programming Language

# 下载

wget https://golang.google.cn/dl/go1.22.2.linux-amd64.tar.gz

# 解压

tar -xvf go1.22.2.linux-arm64.tar.gz -C /usr/local

# 配置环境变量

echo 'export GO111MODULE=on' >> /etc/profile
echo 'export GOROOT=/usr/local/go' >> /etc/profile
echo 'export GOPATH=/home/gopath' >> /etc/profile
echo 'export PATH=$PATH:$GOROOT/bin:$GOPATH/bin' >> /etc/profile

source /etc/profile

# 创建go包安装目录

mkdir -p /home/gopath

# 设置代理

go env -w GOPROXY=https://goproxy.cn,direct

# 检查成功

go version

3.开发工具

推荐goland

二、变量定义与输入输出

1.变量定义

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 先定义,再赋值
  5. var name string
  6. name = "os_lee1"
  7. fmt.Println(name)
  8. // 定义加赋值
  9. var userName string = "os_lee1"
  10. fmt.Println(userName)
  11. }

如果一个变量定义了,但是没有赋值,那么这个变量的值就是这个类型的 "零值" 

  1. // 变量类型省略
  2. var name = "os_lee"
  3. // 简单声明
  4. name := "os_lee"

2.全局变量与局部变量

定义在函数体(包括main函数)内的变量都是局部变量,定义了就必须使用
定义在外部的变量就是全局变量,可以只定义不使用 

  1. package main
  2. import "fmt"
  3. // 全局变量可以不使用
  4. var userName = "oslee_全局"
  5. func main() {
  6. // 局部变量
  7. var name = "oslee_局部"
  8. // 在函数体内定义的变量,必须要使用
  9. fmt.Println(name)
  10. }

3.定义多个变量

  1. package main
  2. import "fmt"
  3. func main() {
  4. var name1, name2, name3 string // 定义多个变量
  5. var a1, a2 = "os", "lee" // 定义多个变量并赋值
  6. a3, a4 := "os", "lee" // 简短定义多个变量并赋值
  7. fmt.Printf("name1: %s, name2: %s, name3: %s\n", name1, name2, name3)
  8. fmt.Printf("a1: %s, a2: %s, a3: %s, a4: %s\n", a1, a2, a3, a4)
  9. }
  1. package main
  2. import "fmt"
  3. var (
  4. name string = "os"
  5. userName = "os_lee"
  6. )
  7. func main() {
  8. fmt.Println(name, userName)
  9. }

4.常量定义

定义的时候就要赋值
赋值之后就不能再修改了

  1. package main
  2. import "fmt"
  3. const name string = "os_lee" // 定义就要赋值
  4. func main() {
  5. fmt.Println(name)
  6. }

5.命名规范

核心思想:首字母大写的变量、函数。方法,属性可在包外进行访问

6.输出

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("os_lee")
  5. fmt.Println(1)
  6. fmt.Println(true)
  7. fmt.Println("什么", "都", "可以", "输出")
  8. }

格式化输出

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Printf("%v\n", "你好") // 可以作为任何值的占位符输出
  5. fmt.Printf("%v %T\n", "os", "lee") // 打印类型
  6. fmt.Printf("%d\n", 3) // 整数
  7. fmt.Printf("%.2f\n", 1.25) // 小数
  8. fmt.Printf("%s\n", "哈哈哈") // 字符串
  9. fmt.Printf("%#v\n", "") // 用go的语法格式输出,很适合打印空字符串
  10. // 还有一个用的比较多的就是将格式化之后的内容赋值给一个变量
  11. name := fmt.Sprintf("%v", "你好")
  12. fmt.Println(name)
  13. }

7.输入 

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("输入您的名字:")
  5. var name string
  6. fmt.Scan(&name) // 这里记住,要在变量的前面加个&, 后面讲指针会提到
  7. fmt.Println("你输入的名字是", name)
  8. }

三、基本数据类型

go语言的基本数据类型有

  1. 整数形
  2. 浮点型
  3. 复数
  4. 布尔
  5. 字符串

1.整数型

go语言的整数类型,具体细分有很多

var n1 uint8 = 2 
var n2 uint16 = 2 
var n3 uint32 = 2 
var n4 uint64 = 2 
var n5 uint = 2 
var n6 int8 = 2 
var n7 int16 = 2 
var n8 int32 = 2 
var n9 int64 = 2 
var n10 int = 2 

大家只需要记住以下几点

  1. 默认的数字定义类型是int类型
  2. 带个u就是无符号,只能存正整数
  3. 后面的数字就是2进制的位数
  4. uint8还有一个别名 byte, 一个字节=8个bit位
  5. int类型的大小取决于所使用的平台

例如uint8,那就是8个二进制位,都用来存储数据,那最小就是0,最大就是2的八次方-1=255

那int8,因为要拿一位存符合,使用实际只有七位可用,所以最小的就是负2的七次方=-128,最大的就是2的七次方-1=127

至于为什么要减一,其实很好理解,因为实际到最后一个数字的时候,已经向前进位了,例如一个小时是60分钟,但是分钟最大只有59

第五点的测试

我是64位操作系统,那么我会试一下int是不是就是int64的最大上限

  1. 263次方-1=9223372036854775807
  2. fmt.Printf("%.0f\n", math.Pow(2, 63))
  3. var n1 int = 9223372036854775807
  4. fmt.Println(n1)
  5. var n2 int = 9223372036854775808 // 看它报不报错
  6. fmt.Println(n2)

2.浮点型

Go语言支持两种浮点型数:float32 和 float64

  • float32 的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32
  • float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64

如果没有显式声明,则默认是float64

3.字符型

注意哦,是字符,不是字符串

比较重要的两个类型是byte(单字节字符)和rune(多字节字符)

  1. package main
  2. import "fmt"
  3. func main() {
  4. var c1 = 'a'
  5. var c2 = 97
  6. fmt.Println(c1) // 直接打印都是数字
  7. fmt.Println(c2)
  8. fmt.Printf("%c %c\n", c1, c2) // 以字符的格式打印
  9. var r1 rune = '中'
  10. fmt.Printf("%c\n", r1)
  11. }

在 Go 中,字符的本质是一个整数,直接输出时,是该字符对应的 UTF-8 编码的码值
可以直接给某个变量赋一个数字,然后按格式化输出时 %c ,会输出该数字对应的 unicode 字符
字符类型是可以进行运算的,相当于一个整数,因为它都对应有 Unicode 码。

4.字符串类型

和字符不一样的是,字符的赋值是单引号,字符串的赋值是双引号 

  1. var s string = "lee李"
  2. fmt.Println(s)

转义字符

一些常用的转义字符

fmt.Println("枫枫\t知道")              // 制表符
fmt.Println("枫枫\n知道")              // 回车
fmt.Println("\"枫枫\"知道")            // 双引号
fmt.Println("枫枫\r知道")              // 回到行首
fmt.Println("C:\\pprof\\main.exe") // 反斜杠

多行字符串

在``这个里面,再出现转义字符就会原样输出了

  1. package main
  2. import "fmt"
  3. func main() {
  4. str := `今天
  5. 天气
  6. 真好
  7. `
  8. fmt.Println(str)
  9. }

5.布尔类型

  • 布尔型数据只有 true(真)和 false(假)两个值
  • 布尔类型变量的默认值为false
  • Go 语言中不允许将整型强制转换为布尔型
  • 布尔型无法参与数值运算,也无法与其他类型进行转换
  • 零值问题
    如果我们给一个基本数据类型只声明不赋值,那么这个变量的值就是对应类型的零值,例如int就是0,bool就是false,字符串就是""

  1. package main
  2. import "fmt"
  3. func main() {
  4. var a1 int
  5. var a2 float32
  6. var a3 string
  7. var a4 bool
  8. fmt.Printf("%#v\n", a1)
  9. fmt.Printf("%#v\n", a2)
  10. fmt.Printf("%#v\n", a3)
  11. fmt.Printf("%#v\n", a4)
  12. }

四、数组、切片、map

1.数组

数组(Array)是一种非常常见的数据类型,几乎所有的计算机编程语言中都会用到它

  1. 数组里的元素必须全部为同一类型,要嘛全部是字符串,要嘛全部是整数
  2. 声明数组时,必须指定其长度或者大小
  1. package main
  2. import "fmt"
  3. func main() {
  4. var array [3]int = [3]int{1, 2, 3}
  5. fmt.Println(array)
  6. var array1 = [3]int{1, 2, 3}
  7. fmt.Println(array1)
  8. var array2 = [...]int{1, 2, 3}
  9. fmt.Println(array2)
  10. }

 如果要修改某个值,只能根据索引去找然后替换

  1. var array1 = [3]int{1, 2, 3}
  2. array1[0] = 10 // 根据索引找到对应的元素位置,然后替换
  3. fmt.Println(array1)

 2.切片

很明显啊,go里面的数组,长度被限制死了,所以不经常用

所以go出了一个数组plus,叫做slice(切片)

切片(Slice)相较于数组更灵活,因为在声明切片后其长度是可变的

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义一个字符串切片
  5. var list []string
  6. list = append(list, "枫枫")
  7. list = append(list, "知道")
  8. fmt.Println(list)
  9. fmt.Println(len(list)) // 切片长度
  10. // 修改第二个元素
  11. list[1] = "不知道"
  12. fmt.Println(list)
  13. }

make函数

除了基本数据类型,其他数据类型如果只定义不赋值,那么实际的值就是nil

  1. // 定义一个字符串切片
  2. var list []string
  3. fmt.Println(list == nil) // true

那么我们可以通过make函数创建指定长度,指定容量的切片了

make([]type, length, capacity)
  1. package main
  2. import "fmt"
  3. func main() {
  4. // 定义一个字符串切片
  5. var list = make([]string, 0)
  6. fmt.Println(list, len(list), cap(list))
  7. fmt.Println(list == nil) // false
  8. list1 := make([]int, 2, 2)
  9. fmt.Println(list1, len(list1), cap(list1))
  10. }

为什么叫切片?
因为切片是数组切出来的

  1. package main
  2. import "fmt"
  3. func main() {
  4. var list = [...]string{"a", "b", "c"}
  5. slices := list[:] // 左一刀,右一刀 变成了切片
  6. fmt.Println(slices)
  7. fmt.Println(list[1:2]) // b
  8. }

切片排序

  1. package main
  2. import (
  3. "fmt"
  4. "sort"
  5. )
  6. func main() {
  7. var list = []int{4, 5, 3, 2, 7}
  8. fmt.Println("排序前:", list)
  9. sort.Ints(list)
  10. fmt.Println("升序:", list)
  11. sort.Sort(sort.Reverse(sort.IntSlice(list)))
  12. fmt.Println("降序:", list)
  13. }

切片面试题 

面试题1:什么是Go语言中的切片?

答案: Go语言中的切片(slices)是一种灵活的、动态大小的、基于数组的抽象数据类型。它并不存储任何数据,而是指向底层数组的一个片段,包含了三个信息:指向数组的指针、长度(len)和容量(cap)。长度表示切片当前拥有的元素个数,容量则是切片可以扩展到的最大元素个数,容量等于或大于长度。

面试题2:切片的扩容是如何工作的?

答案: 当向切片添加元素超出其当前容量时,Go语言会自动扩容切片。扩容的具体策略并不是固定的,但大致遵循以下规则:

  • 如果切片的容量不足以容纳新增元素,Go 会创建一个新的更大的底层数组,将原有数据复制到新数组,并更新切片的指针和容量。
  • 扩容因子通常为原来容量的2倍,但是也可能会根据实际情况调整,比如在Go 1.17版本之后,根据内存分配器的行为,扩容可能会跳跃式增长以更好地适应内存分配器的粒度。
  • 新容量至少会增加到原来的两倍+所需添加的元素数量,以尽量减少频繁的扩容操作。

面试题3:切片与数组有什么区别?

答案

  1. 长度可变性

    • 数组(array)的长度在声明时确定并且不能改变。
    • 切片(slice)虽然在声明时可以指定初始长度,但可以在运行时动态改变其长度(增删元素),不过容量有限制。
  2. 存储结构

    • 数组是一个定长的、连续的内存区域。
    • 切片不是数据结构,它只是一个描述符,指向一个数组的一部分。
  3. 引用行为

    • 数组变量直接存储数据,赋值操作会复制整个数组的内容。
    • 切片变量存储的是指向数组的指针和长度、容量信息,赋值操作只会复制切片描述符,不会复制底层数据。

面试题4:浅拷贝和深拷贝

浅拷贝(Shallow Copy): 浅拷贝是创建一个新的切片,但它仍然指向同一个底层数组。这意味着对新切片所做的修改会影响到原始切片所指向的数据。

// 示例1:直接赋值
original := []int{1, 2, 3}
copied := original // 此时copied是对original的浅拷贝

// 示例2:使用内置的copy函数
original := []int{1, 2, 3}
copied := make([]int, len(original))
copy(copied, original) // 这也是浅拷贝,copied和original共享相同的底层数组

深拷贝(Deep Copy): 深拷贝是创建一个与原始切片完全独立的新切片,包含一个全新的底层数组。对新切片所做的修改不会影响到原始切片。

// 手动循环遍历复制每一个元素
original := []int{1, 2, 3}
copied := make([]int, len(original))
for i, v := range original {
copied[i] = v
}

// 或者使用反射(reflect)包,但请注意这不是最优实践,仅作演示
import "reflect"
original := []int{1, 2, 3}
copied := reflect.ValueOf(original).Clone().Interface().([]int)

3.Map

  1. Go语言中的map(映射、字典)是一种内置的数据结构,它是一个无序的key-value对的集合
  2. map的key必须是基本数据类型,value可以是任意类型
  3. 注意,map使用之前一定要初始化
  1. package main
  2. import "fmt"
  3. func main() {
  4. // 声明
  5. var m1 map[string]string
  6. // 初始化1
  7. m1 = make(map[string]string)
  8. // 初始化2
  9. m1 = map[string]string{}
  10. // 设置值
  11. m1["name"] = "枫枫"
  12. fmt.Println(m1)
  13. // 取值
  14. fmt.Println(m1["name"])
  15. // 删除值
  16. delete(m1, "name")
  17. fmt.Println(m1)
  18. // 声明并赋值
  19. var m2 = map[string]string{}
  20. fmt.Println(m2)
  21. var m3 = make(map[string]string)
  22. fmt.Println(m3)
  23. }

map取值

  1. 如果只有一个参数接,那这个参数就是值,如果没有,这个值就是类型的零值
  2. 如果两个参数接,那第二个参数就是布尔值,表示是否有这个元素
  1. package main
  2. import "fmt"
  3. func main() {
  4. // 声明并赋值
  5. var m1 = map[string]int{
  6. "age": 21,
  7. }
  8. age1 := m1["age1"] // 取一个不存在的
  9. fmt.Println(age1)
  10. age2, ok := m1["age1"]
  11. fmt.Println(age2, ok)
  12. }

map面试题 

面试题1:什么是Go语言中的map?

答案: Go语言中的map是一种关联数组或字典类型的数据结构,它存储键值对(key-value pairs),通过键(key)快速查找对应的值(value)。键和值可以是任何类型,但键的类型必须支持相等比较,通常为整型、浮点型、字符串或复合类型(如结构体,但结构体内的字段必须支持相等比较)。

面试题2:Go语言中的map何时会引发panic?

答案

  • 在使用尚未初始化的map时(即nil map)执行读写操作,会导致panic。
  • 在迭代map的过程中,如果同时修改该map,也会导致panic,除非使用for range循环迭代并在循环体内使用delete函数删除元素。

面试题3:Go语言中map的扩容是如何进行的?

Go语言中的map在底层实现上使用了哈希表。当map中的元素数量越来越多,达到一定的负载因子时,Go语言会自动触发map的扩容操作。扩容是为了保持map操作的高效性,防止哈希冲突过于密集,导致性能下降。

扩容的具体流程如下:

  1. 负载因子判断: 当map的元素数量(entry count)与其桶(bucket)数量的比例超过一定阈值时(通常大约是6.5),map会触发扩容。这个阈值可以通过runtime包的内部常量mapExpandHeapSizemapExpandLoadFactor间接推算出来。

  2. 新桶分配: 扩容时,Go会创建一个新的、大小翻倍的哈希表。例如,如果原map的桶数量是2^N,则新表的桶数量将是2^(N+1)。

  3. 元素迁移: Go采用了渐进式(incremental)的迁移策略,不会一次性将所有元素从旧表迁移到新表。在每次map的读写操作时,如果发现正在进行扩容操作,就会顺带将旧表中的部分元素迁移到新表中。每次操作最多迁移两个桶(bucket)的数据。

  4. 保持引用关系: 在扩容过程中,map的旧表和新表会同时存在,直到所有元素都迁移到新表为止。旧表的最后一个桶会存储一个指向新表的指针,以确保在扩容过程中依然能正确找到已迁移的元素。

  5. 空间回收: 所有元素都迁移到新表后,旧表的空间最终会被垃圾回收机制释放。

值得注意的是,上述细节基于Go语言的早期版本,Go语言的map扩容机制在不同版本间可能会有所调整。最新的Go版本可能会根据具体情况采用不同的扩容策略和负载因子阈值。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/406531
推荐阅读
相关标签
  

闽ICP备14008679号