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.变量定义

package mainimport "fmt"func main() {// 先定义,再赋值var name stringname = "os_lee1"fmt.Println(name)// 定义加赋值var userName string = "os_lee1"fmt.Println(userName)
}

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

// 变量类型省略
var name = "os_lee"// 简单声明
name := "os_lee"

2.全局变量与局部变量

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

package mainimport "fmt"// 全局变量可以不使用
var userName = "oslee_全局" func main() {// 局部变量var name = "oslee_局部"// 在函数体内定义的变量,必须要使用fmt.Println(name)
}

3.定义多个变量

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

4.常量定义

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

package mainimport "fmt"const name string = "os_lee" // 定义就要赋值func main() {fmt.Println(name)
}

5.命名规范

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

6.输出

package mainimport "fmt"func main() {fmt.Println("os_lee")fmt.Println(1)fmt.Println(true)fmt.Println("什么", "都", "可以", "输出")
}

格式化输出

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

7.输入 

package mainimport "fmt"func main() {fmt.Println("输入您的名字:")var name stringfmt.Scan(&name) // 这里记住,要在变量的前面加个&, 后面讲指针会提到fmt.Println("你输入的名字是", name)
}

三、基本数据类型

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的最大上限

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

2.浮点型

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

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

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

3.字符型

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

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

package mainimport "fmt"func main() {var c1 = 'a'var c2 = 97fmt.Println(c1) // 直接打印都是数字fmt.Println(c2)fmt.Printf("%c %c\n", c1, c2) // 以字符的格式打印var r1 rune = '中'fmt.Printf("%c\n", r1)
}

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

4.字符串类型

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

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

转义字符

一些常用的转义字符

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

多行字符串

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

package mainimport "fmt"func main() {str := `今天
天气
真好
`fmt.Println(str)
}

5.布尔类型

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

package mainimport "fmt"func main() {var a1 intvar a2 float32var a3 stringvar a4 boolfmt.Printf("%#v\n", a1)fmt.Printf("%#v\n", a2)fmt.Printf("%#v\n", a3)fmt.Printf("%#v\n", a4)
}

四、数组、切片、map

1.数组

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

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

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

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

 2.切片

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

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

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

package mainimport "fmt"func main() {// 定义一个字符串切片var list []stringlist = append(list, "枫枫")list = append(list, "知道")fmt.Println(list)fmt.Println(len(list)) // 切片长度// 修改第二个元素list[1] = "不知道"fmt.Println(list)
}

make函数

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

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

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

make([]type, length, capacity)
package mainimport "fmt"func main() {// 定义一个字符串切片var list = make([]string, 0)fmt.Println(list, len(list), cap(list))fmt.Println(list == nil) // falselist1 := make([]int, 2, 2)fmt.Println(list1, len(list1), cap(list1))
}

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

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

切片排序

package mainimport ("fmt""sort"
)func main() {var list = []int{4, 5, 3, 2, 7}fmt.Println("排序前:", list)sort.Ints(list)fmt.Println("升序:", list)sort.Sort(sort.Reverse(sort.IntSlice(list)))fmt.Println("降序:", list)
}

切片面试题 

面试题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使用之前一定要初始化
package mainimport "fmt"func main() {// 声明var m1 map[string]string// 初始化1m1 = make(map[string]string)// 初始化2m1 = map[string]string{}// 设置值m1["name"] = "枫枫"fmt.Println(m1)// 取值fmt.Println(m1["name"])// 删除值delete(m1, "name")fmt.Println(m1)// 声明并赋值var m2 = map[string]string{}fmt.Println(m2)var m3 = make(map[string]string)fmt.Println(m3)}

map取值

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

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版本可能会根据具体情况采用不同的扩容策略和负载因子阈值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/617392.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字人生成 Wav2Lip面部动画 神经辐射场 NeRF场景结构 3DMM人脸模型 深度学习 生成对抗网 GAN 语音交互、虚拟现实(VR)和增强现实(AR)

数字人生成 喝奶茶的甄嬛 数字人,从广义上来说,是数字技术在人体解剖、物理、生理及智能各个层次、各个阶段的渗透。它是信息科学与生命科学融合的产物,利用信息科学的方法对人体在不同水平的形态和功能进行虚拟仿真。数字人可以是虚拟人物,也可以是真实人物的数字再现,它…

Typora导入功能使用详细

一、 pandoc安装(导入需要的插件) 1. 首次安装完typora,是没法导入的,需要安装pandoc,首先我们先在文件夹里面新建一个Typora文件,然后再找到导入功能点击就可以弹出安装的地址了 2. 点击文件可以找到导入…

常用算法——双指针算法

双指针算法介绍: 所谓的双指针算法看似十分的神秘,但是实质上就是两个标志查找元素的变量。双指针既可以是我们平常最常说的指针(类似int *类型的数据),也可以是数组的下标。因为对于一个数组数据的查找,通…

halcon-轴断面检测定位

前言 通常情况下轴检测时,通常会检测轴的各个阶段的长度。但是由于各种原因,在轴断面的区域现实不明显,无法正确提取,这时候需要根据轴断面的突出部分进行检测,但是由于部分轴的粗轴和细轴区域的宽度差距相当接近&…

安卓数据怎么恢复?十大顶级Android数据恢复软件

Android 是移动设备的顶级操作系统。由于许多不确定的情况,会发生数据丢失。数据恢复软件有助于挽救丢失的数据。在这里,让我们讨论一下 前 10 名最佳 android 数据恢复软件。 十大顶级Android数据恢复软件 1.奇客数据恢复 奇客数据恢复是由奇客软件软件…

Blender4.0 下载地址及安装教程

Blender是一款开源的3D计算机图形软件,广泛应用于动画制作、游戏开发、建模、渲染等领域。它提供了一套强大的工具和功能,让用户能够进行三维建模、动画制作和视觉效果的创作。 Blender支持多种文件格式的导入和导出,使用户能够与其他软件进…

浮点数的表示

王道考研ppt总结: 二、个人理解 浮点数解决的是定点数的位数局限,导致表示范围有限的问题 阶码:由阶符和数值部分组成,阶符为,小数点向左移动,否则向右移动;数值部分,是底数的几次幂…

【CSS】一篇文章讲清楚screen、window和html元素的位置:top、left、width、height

一个Web网页从内到外的顺序是: 元素div,ul,table... → 页面body → 浏览器window → 屏幕screen 分类详情屏幕screen srceen.width - 屏幕的宽度 screen.height - 屏幕的高度(屏幕未缩放时,表示屏幕分辨率) screen.availLeft …

Electron 桌面端应用的使用 ---前端开发

Electron是什么? Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。 入门…

Spring Boot - 利用MDC(Mapped Diagnostic Context)实现轻量级同步/异步日志追踪

文章目录 Pre什么是MDC(Mapped Diagnostic Context)Slf4j 和 MDC基础工程工程结构POMlogback-spring.xmlapplication.yml同步方式方式一: 拦截器自定义日志拦截器添加拦截器 方式二: 自定义注解 AOP自定义注解 TraceLog切面 测试…

解析器模式:面向对象设计中的表达式评估与语法树构建策略

解释器模式(Interpreter Pattern)是一种行为设计模式,它定义了一种语言,并使用该语言来解释句子。这种模式用于描述如何构成一个简单的语言解析器。在Java中实现解释器模式通常涉及一个抽象表达式接口、具体表达式类(终…

HCIP课后习题之一

1、路由协议用工作机制上分为那几种?分别是? A:两种。分别是静态路由和动态路由(可分为IGP和EGP) 2、IGP和EGP协议有哪些? A:IGP: RIP、OSPF、ISIS、EIGRP EGP: BGP 3、路由优先级的用途&…