极速Go语言入门(超全超详细)-基础篇

简介: 极速Go语言入门(超全超详细)-基础篇

文章目录


文章目录

Golang概述

Go语言三大牛

谷歌创造Golang的原因

Golang 的发展历程

Golang 的语言的特点

Go语言开发工具

Go开发环境配置(sdk下载及配置)

使用开发工具创建第一个Go项目

Go 程序开发的注意事项

官方参考文档

Go学习

Go变量

数据类型

基本数据类型说明

浮点型

字符

字符类型本质探讨

布尔型

字符串

指针

值类型与引用类型

相关示例代码

运行结果

指针相关图示

标识符

概念

标识符的命名规则

保留关键字

预留关键字

运算符

算数运算符一览表

关系运算符

细节说明

逻辑运算符

赋值运算符

位运算符

其他运算符

特别说明(三元运算符)

运算符优先级

键盘输入语句

代码示例

运行结果

程序流程控制

顺序流程

分支控制

循环控制

跳转控制(break/continue/goto)

生成随机数

数组

切片

映射(map)

函数


文章目录

Golang概述


Go语言三大牛

image.png

谷歌创造Golang的原因

image.png

Golang 的发展历程

2007 年,谷歌工程师 Rob Pike, Ken Thompson 和 Robert Griesemer 开始设计一门全新的语言,这是 Go 语言的最初原型。

2009 年 11 月 10 日,Google 将 Go 语言以开放源代码的方式向全球发布。

2015 年 8 月 19 日,Go 1.5 版发布,本次更新中移除了”最后残余的 C 代码”

2017 年 2 月 17 日,Go 语言 Go 1.8 版发布。

2017 年 8 月 24 日,Go 语言 Go 1.9 版发布。 1.9.2 版本

2018 年 2 月 16 日,Go 语言 Go 1.10 版发布。

Golang 的语言的特点

Go 语言保证了既能到达静态编译语言的安全和性能,又达到了动态语言开发维护的高效率 ,使用一个表达式来形容 Go 语言:Go = C + Python , 说明 Go 语言既有 C 静态语言程序的运行速度,又能达到 Python 动态语言的快速开发。


从 C 语言中继承了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,

等,也保留了和 C 语言一样的编译执行方式及弱化的指针


举一个案例(体验):

//go 语言的指针的使用特点(体验)

func testPtr(num *int) {

*num = 20

}

  1. 引入包的概念,用于组织程序结构,Go 语言的一个文件都要归属于一个包,而不能单独存在。
package main //一个文件都要归属于一个包
import "fmt"
func main() {
  fmt.Println("hello word")
}

垃圾回收机制,内存自动回收,不需开发人员管理

天然并发 (重要特点)

从语言层面支持并发,实现简单

goroutine,轻量级线程,可实现大并发处理,高效利用多核。

基于 CPS 并发模型(Communicating Sequential Processes )实现

吸收了管道通信机制,形成 Go 语言特有的管道 channel 通过管道 channel , 可以实现不同的 goroute

之间的相互通信。


函数可以返回多个值。举例:

//写一个函数,实现同时返回 和,差

//go 函数支持返回多个值

func getSumAndSub(n1 int, n2 int) (int, int ) {

sum := n1 + n2 //go 语句后面不要带分号.

sub := n1 - n2

return sum , sub

}


新的创新:比如切片 slice、延时执行 defer

Go语言开发工具

我使用的是GoLand开发工具,比较顺手

GoLand 是 JetBrains 公司推出的 Go 语言集成开发环境。GoLand 同样基于 IntelliJ 平台开发,支持 JetBrains 的插件体系。


下载地址:https://www.jetbrains.com/go/download/other.html

自己选择喜欢的版本和相应的系统下载,不建议使用太新的版本,我自己用2021.3.5版本比较顺手


账号的话可以去淘宝买教育注册渠道,注册后可以使用一年

或者使用插件无限使用30天(⚠️注意21年及之前的可以使用,相对新的版本不支持),相关文档地址:https://blog.csdn.net/qq_37699336/article/details/116528062


Go开发环境配置(sdk下载及配置)

SDK 下载地址:Golang 中国 https://www.golangtc.com/download

sdk配置相关参考文档:https://blog.csdn.net/m0_49589654/article/details/127259754


使用开发工具创建第一个Go项目

创建一个项目,选择好sdk的路径就可以了

1.png

右击项目文件创建一个go file

1.png

image.png

之后写个hello word

package main //一个文件都要归属于一个包
import "fmt"
func main() {
  fmt.Println("hello word")
}

运行

1.png

Go 程序开发的注意事项

Go 源文件以 “go” 为扩展名。

Go 应用程序的执行入口是 main()函数。 这个是和其它编程语言(比如 java/c)

Go 语言严格区分大小写。

Go 方法由一条条语句构成,每个语句后不需要分号(Go 语言会在每行后自动加分号),这也体现出 Golang 的简洁性。

Go 编译器是一行行进行编译的,因此我们一行就写一条语句,不能把多条语句写在同一个,否

则报错


go 语言定义的变量或者 import 的包如果没有使用到,代码不能编译通过。

大括号都是成对出现的,缺一不可。


官方参考文档

Golang 官方网站(可能访问不了): https://golang.org

Golang 中文网 在线标准库文档: https://studygolang.com/pkgdoc


Go学习

Go变量

概念:变量相当于内存中一个数据存储空间的表示,你可以把变量看做是一个房间的门牌号,通过门牌号我们可以找到房间,同样的道理,通过变量名可以访问到变量 (值)。


变量的使用步骤:声明变量(也叫:定义变量) -> 非变量赋值 -> 使用变量

package main
import "fmt"
func main() {
  //创建变量
  var a int
  //变量赋值
  a = 10
  //使用变量(进行打印)
  fmt.Println("a = ", a)
}

运行结果

a = 10
package main
import fmt "fmt"
// /全局变量///
var age, name = 18, "小明"
///全局变量///
func main() {
    //创建变量
  //变量=变量名+值+数据类型,这一点请大家注意,变量的三要素
  //Golang 的变量如果没有赋初值,编译器会使用默认值, 比如 int 默认值 0 string 默认值为空串, 小数默认为 0
  ///局部变量///
  //1、创建变量但 不赋值 使用的默认值(int 默认值为0)
  var a int
  //使用变量(进行打印)
  fmt.Println("a = ", a)
  //2、创建变量但不赋予类型,根据值自行判定变量类型(类型推导)
  var b = 10
  //fmt.Printf格式化输出 %v:输出值, %T:输出值的类型 \n:换行
  fmt.Printf("b = %v, type: %T  \n", b, b)
  //3、使用:=声明变量
  //c := "Go" 等价于 var c string = "Go";
  c := "Go"
  fmt.Printf("c = %v, type: %T \n", c, c)
  //4、多变量声明,带值声明或者不带值声明
  var d, e, f int              //不带值
  var d1, e1, f1 int = 1, 2, 3 //带值(相同类型)
  d2, e2, f2 := 1, "123", 's'  //带值(相同类型)
  fmt.Println(d, e, f, "------", d1, e1, f1, "------", d2, e2, f2)
  //注意⚠️:在同一代码块中,不能对类型再次赋予类型
  //var testA int = 10;
  //testA  := 20; // ❌
  ///局部变量///
  //输出全局变量
  fmt.Println(age, name)
}

运行结果

a =  0
b = 10, type: int  
c = %!d(string=Go), type: string 
0 0 0 ------ 1 2 3 ------ 1 123 115
18 小明

数据类型

1.png

基本数据类型说明

类型

描述
uint 32位或64位
uint8 无符号 8 位整型 (0 到 255)
uint16 无符号 16 位整型 (0 到 65535)
uint32 无符号 32 位整型 (0 到 4294967295)
uint64 无符号 64 位整型 (0 到 18446744073709551615)
int 32位或64位
int8 有符号 8 位整型 (-128 到 127)
int16 有符号 16 位整型 (-32768 到 32767)
int32 有符号 32 位整型 (-2147483648 到 2147483647)
int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
byte uint8的别名(type byte = uint8)
rune int32的别名(type rune = int32),表示一个unicode码
uintptr 无符号整型,用于存放一个指针是一种无符号的整数类型,没有指定具体的bit大小但是足以容纳指针。
uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。


float32 IEEE-754 32位浮点型数
float64 IEEE-754 64位浮点型数
complex64 32 位实数和虚数
complex128 64 位实数和虚数

整型数据

分为两类,有符号和无符号两种类型


有符号: int, int8, int16, int32, int64

无符号: uint, uint8, uint16, uint32, uint64, byte

不同位数的整型区别在于能保存整型数字范围的大小;

有符号类型可以存储任何整数,无符号类型只能存储自然数

int和uint的大小和系统有关,32位系统表示int32和uint32,如果是64位系统则表示int64和uint64

byte与uint8类似,一般用来存储单个字符

在保证程序正确运行下,尽量使用占用空间小的数据类型

fmt.Printf(“%T”, var_name)输出变量类型

unsafe.Sizeof(var_name)查看变量占用字节


浮点型

浮点型也就是小数类型,可以存放小数。比如6.6,-12.34

1、关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位

2、尾数部分可能丢失,造成精度损失。-123.0000901

3、浮点型的存储分为三部分:符号位+指数位+尾数位,在存储过程中,精度会有丢失

4、golang的浮点型默认为float64类型

5、通常情况下,应该使用float64,因为它比float32更精确

6、0.123可以简写成.123,也支持科学计数法表示:5.1234e2 等价于512.34


字符

Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的,也就是说对于传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的。


字符只能被单引号包裹,不能用双引号,双引号包裹的是字符串

当我们直接输出type值时,就是输出了对应字符的ASCII码值

当我们希望输出对应字符,需要使用格式化输出

Go语言的字符使用UTF-8编码,英文字母占一个字符,汉字占三个字符

在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值。

可以直接给某个变量赋一个数字,然后按格式化输出时%c,会输出该数字对应的unicode字符

字符类型是可以运算的,相当于一个整数,因为它们都有对应的unicode码

字符类型本质探讨

字符型存储到计算机中,需要将字符对应的码值(整数)找出来存储:字符 --> 码值 --> 二进制 --> 存储读取: 二进制 -->码值 --> 字符 --> 读取

字符和码值的对应关系是通过字符编码表决定的(是规定好的)

Go语言的编码都统一成了UTF-8。非常的方便,很统一,再也没有编码乱码的困扰了

布尔型

布尔类型也叫做bool类型,bool类型数据只允许取值true或false

bool类型占1个字节

bool类型适用于逻辑运算,一般用于流程控制


字符串

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本

1、字符串一旦赋值了,就不能修改了:在Go中字符串是不可变的。

2、字符串的两种标识形式


指针

基本数据类型,变量存的就是值,也叫值类型

获取变量的地址,用&,比如var num int,获取num的地址:&num

指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值,比如:var ptr *int = &num

获取指针类型所指向的值,使用:*,比如,var ptr int,使用ptr获取ptr指向的值

指针细节说明:


值类型,都有对应的指针类型,形式为数据类型,比如int对应的指针就是int,float64对应的指针类型就是*float64,依此类推。

值类型包括:基本数据类型、数组和结构体struct


值类型与引用类型

值类型和引用类型使用特点:

值类型:变量直接存储值,内存通常在栈中分配

引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量应用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。

Golang中值类型和引用类型的区分


值类型:基本数据类型(int系列、float系列、bool、string)、数组和结构体

引用类型:指针、slice切片、map、管道chan、interface等都是引用类型

相关示例代码

package main
import (
  "fmt"
  "strconv"
  "unsafe"
)
func main() {
    println("----byte类型----")
  //Golang 程序中整型变量在使用时,遵守保小不保大的原则,即:在保证程序正确运行下,尽量 使用占用空间小的数据类型。【如:年龄】
  //bit: 计算机中的最小存储单位。byte:计算机中基本存储单元。[二进制再详细说] 1byte = 8 bit
  var n0 byte = 10
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n0, n0, unsafe.Sizeof(n0))
  println("----int类型----")
  //int 类型
  var n1 int = 20
  var n2 int8 = 21
  var n3 int16 = 22
  var n4 int32 = 23
  var n5 int64 = 24
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n1, n1, unsafe.Sizeof(n1))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n2, n2, unsafe.Sizeof(n2))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n3, n3, unsafe.Sizeof(n3))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n4, n4, unsafe.Sizeof(n4))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", n5, n5, unsafe.Sizeof(n5))
  println("----float类型----")
  //float 类型
  var f1 float32 = 30.23748734872346 //会损失精度,如果我们要保存一个精度高的数,则应该选用 float64
  var f2 float64 = 21.23748734872346
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", f1, f1, unsafe.Sizeof(f1))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", f2, f2, unsafe.Sizeof(f2))
  println("----字符类型----")
  //字符类型(英文字母占1 个字节 汉字占3 个字节), 可以直接给某个变量赋一个数字,然后按格式化输出时%c,会输出该数字对应的 unicode 字符
  //Go 语 言 的 字 符 使 用 UTF-8 编 码 , 如 果 想 查 询 字 符 对 应 的 utf8 码 值 http://www.mytju.com/classcode/tools/encode_utf8.asp
  //不理解utf-8、ASCII和Unicode可以看考这篇文章:https://www.bilibili.com/read/cv16001311/
  var c0 int = 's'
  var c1 int = '北'
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d, 显示信息=%c \n", c0, c0, unsafe.Sizeof(c0), c0)
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d, 显示信息=%c \n", c1, c1, unsafe.Sizeof(c1), c1)
  println("----布尔类型----")
  //bool 类型占 1 个字节。适于逻辑运算,一般用于程序流程控制
  var flagControl bool = true
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", flagControl, flagControl, unsafe.Sizeof(flagControl))
  for i := 0; i < 100; i++ {
    if i == 10 {
      //do something
      flagControl = false
    }
    if flagControl == false {
      fmt.Println("结束循环, i= ", i)
      break
    }
  }
  println("----string 类型----")
  //字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本
  //Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本,这样 Golang 统一使用 UTF-8 编码,中文 乱码问题不会再困扰程序员。
  var str string = "测试字符串"
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", str, str, unsafe.Sizeof(str))
  //注意⚠️:字符串一旦赋值了,字符串就不能修改了:在 Go 中字符串是不可变的。
  //var str1 string = "abc"
  //str1[0] = 'c' //❌ 不可取,不可修改里面的内容
  //字符串的两种表示形式 (1) 双引号, 会识别转义字符 (2) 反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果 【案例演示】
  //方式1
  var str2 string = "测试字符串"
  //方式2
  var str3 string = `
      测试字符串表示形式,
      我是以字符串的原生形式输出,可以实现防止攻击、输出源代码等效果
      var str4 string = "4"
  `
  //字符串拼接方式 "" + "",当一行字符串太长时,需要使用到多行字符串,可以如下处理
  var str5 string = "hello" + "word" + "hello" + "word" + "hello" + "word" +
    "test" + "str"
  fmt.Println(str2, str3, str5)
  println("----指针类型----")
  //基本数据类型,变量存的就是值,也叫值类型。 获取变量的地址,用&,比如: var num int, 获取 num 的地址:&num
  //指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值
  var i int = 10
  //1、ptr是一个指针变量 2、ptr存的是i变量的地址 3、类型是*int
  var ptr *int = &i
  fmt.Printf("指针存储的值:%v, 类型:%T, 占内存字节数:%d, 指针存储地址指向的值:%d, 指针的地址:%v \n", ptr, ptr, unsafe.Sizeof(ptr), *ptr, &ptr)
  println("-----基本数据类型的相互转换----")
  基本数据类型的相互转换///
  //Golang 和 java / c 不同,Go 在不同类型的变量之间赋值时需要显式转换。也就是说 Golang 中数 据类型不能自动转换。
  var int_1 int = 100
  var float1 float32 = float32(int_1)
  var int_2 int8 = int8(int_1)
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", float1, float1, unsafe.Sizeof(float1))
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", int_2, int_2, unsafe.Sizeof(int_2))
  //在转换中,比如将 int64 转成 int8 【-128---127】 ,编译时不会报错,只是转换的结果是按 溢出处理,和我们希望的结果不一样。 因此在转换时,需要考虑范围.
  var int_3 int64 = 999
  var int_4 int8 = int8(int_3) //转换的时候值溢出
  fmt.Printf("值:%v, 类型:%T, 占内存字节数:%d \n", int_4, int_4, unsafe.Sizeof(int_4))
  //基本类型和string互转
  //方式 1:fmt.Sprintf("%参数", 表达式)
  var number1 int = 999
  var number2 float64 = 999.999
  var bool_1 bool = false
  var byte_1 byte = 'a'
  var str_1 string
  str_1 = fmt.Sprintf("%d", number1)
  fmt.Printf("1、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  str_1 = fmt.Sprintf("%f", number2)
  fmt.Printf("2、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  str_1 = fmt.Sprintf("%t", bool_1)
  fmt.Printf("3、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  str_1 = fmt.Sprintf("%c", byte_1)
  fmt.Printf("4、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  //方式 2:使用 strconv 包的函数
  var number4 int32 = 888
  var number5 float64 = 888.888
  var bool_2 bool = false
  str_1 = strconv.FormatInt(int64(number4), 10)
  fmt.Printf("5、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  //FormatFloat(f float64, fmt byte, prec, bitSize int),参数依次表示为转换的值、转换的格式、保留小数后多少位、是转换成float64还是float32
  str_1 = strconv.FormatFloat(number5, 'f', 2, 64)
  fmt.Printf("6、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  str_1 = strconv.FormatBool(bool_2)
  fmt.Printf("7、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", str_1, str_1, unsafe.Sizeof(str_1))
  //string 类型转基本数据类型
  var str_2 string = "true"
  var bool_3 bool
  //strconv.ParseBool返回两个值, _标识忽略返回的第二个值
  bool_3, _ = strconv.ParseBool(str_2)
  fmt.Printf("8、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", bool_3, bool_3, unsafe.Sizeof(bool_3))
  //  strconv.ParseFloat()同理
  //  strconv.ParseInt()同理
  //  strconv.ParseUint()同理
  //注意⚠️事项:在将 String 类型转成 基本数据类型时,要确保 String 类型能够转成有效的数据
  //比如 我们可以 把 "123" ,转成一个整数,但是不能把 "hello" 转成一个整数,如果这样做,Golang 直接将其转成 0 , 其它类型也是一样的道理. float => 0 bool => false
  var str_4 string = "hello"
  var int_5 int64
  int_5, _ = strconv.ParseInt(str_4, 10, 64)
  fmt.Printf("9、转换后的值:%v, 类型:%T, 占内存字节数:%d \n", int_5, int_5, unsafe.Sizeof(int_5))
}

运行结果

----byte类型----
值:10, 类型:uint8, 占内存字节数:1 
----int类型----
值:20, 类型:int, 占内存字节数:8 
值:21, 类型:int8, 占内存字节数:1 
值:22, 类型:int16, 占内存字节数:2 
值:23, 类型:int32, 占内存字节数:4 
值:24, 类型:int64, 占内存字节数:8 
----float类型----
值:30.237488, 类型:float32, 占内存字节数:4 
值:21.23748734872346, 类型:float64, 占内存字节数:8 
----字符类型----
值:115, 类型:int, 占内存字节数:8, 显示信息=s 
值:21271, 类型:int, 占内存字节数:8, 显示信息=北 
----布尔类型----
值:true, 类型:bool, 占内存字节数:1 
结束循环, i=  10
----string 类型----
值:测试字符串, 类型:string, 占内存字节数:16 
测试字符串 
                        测试字符串表示形式,
                        我是以字符串的原生形式输出,可以实现防止攻击、输出源代码等效果
                        var str4 string = "4"
         hellowordhellowordhellowordteststr
----指针类型----
指针存储的值:0x14000126050, 类型:*int, 占内存字节数:8, 指针存储地址指向的值:10, 指针的地址:0x14000120020 
-----基本数据类型的相互转换----
值:100, 类型:float32, 占内存字节数:4 
值:100, 类型:int8, 占内存字节数:1 
值:-25, 类型:int8, 占内存字节数:1 
1、转换后的值:999, 类型:string, 占内存字节数:16 
2、转换后的值:999.999000, 类型:string, 占内存字节数:16 
3、转换后的值:false, 类型:string, 占内存字节数:16 
4、转换后的值:a, 类型:string, 占内存字节数:16 
5、转换后的值:888, 类型:string, 占内存字节数:16 
6、转换后的值:888.89, 类型:string, 占内存字节数:16 
7、转换后的值:false, 类型:string, 占内存字节数:16 
8、转换后的值:true, 类型:bool, 占内存字节数:1 
9、转换后的值:0, 类型:int64, 占内存字节数:8 

指针相关图示

1.png

标识符

概念

Golang 对各种变量、方法、函数等命名时使用的字符序列称为标识符 ,凡是自己可以起名字的地方都叫标识符。


标识符的命名规则

由 26 个英文字母大小写,0-9 ,_ 组成

数字不可以开头。var num int //ok var 3num int //error

Golang 中严格区分大小写。

在 golang 中,num 和 Num 是两个不同的变量 (var num int

,var Num int )


标识符不能包含空格。

下划线"_"本身在 Go 中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但

是它对应的值会被忽略(比如:忽略某个返回值)。所以仅能被作为占位符使用,不能作为标识符使用


不能以系统保留关键字作为标识符(一共有 25 个),比如 break,if 等等…

保留关键字

1.png

预留关键字

1.png

运算符

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等

算数运算符一览表

1.png

1.png

package main
import (
  "fmt"
)
func main() {
  var num_0 int = 100
  var num_1 int = 100
  var num_2 int = num_1 + num_0/num_0*num_1
  println(num_2)
  // 演示 % 的使用
  //看一个公式 a % b = a - a / b * b
  fmt.Println("10%3=", 10%3)
  fmt.Println("-10%3=", -10%3)
  fmt.Println("10%-3=", 10%-3)
  fmt.Println("-10%-3=", -10%-3)
  //i++, i--
  var i_0 int8 = 0
  i_0++
  var i_1 int8 = 0
  i_1--
  fmt.Printf("i_0 = %d, i_1 = %d \n", i_0, i_1)
  //Golang 的自增自减只能当做一个独立语言使用时,不能这样使
  /**
  var i_3 int = i_0 ++    //❌ 错误用法
  if i_0 ++ > 1{}    //❌ 错误用法
  */
}

关系运算符

1.png

细节说明

关系运算符的结果都是 bool 型,也就是要么是 true,要么是 false。

关系运算符组成的表达式,我们称为关系表达式: a > b

比较运算符"=="不能误写成 “=” !!

逻辑运算符

用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个 bool 值

1.png

1) &&也叫短路与如果第一个条件为 false,则第二个条件不会判断,最终结果为 **false **

  1. ||也叫短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为 true
  package main
  func main() {
  //逻辑运算符
  var number_0 int8 = 10
  var number_1 int8 = 15
  if number_1 == 11 || number_0 < 10 {
    println("我是1")
  } else if number_1 == 15 && number_0 == 10 {
    println("我是2")
  } else if !(number_1 == 15) {
    println("我是3")
  }
  }

赋值运算符

1.png

1.png

位运算符

位运算符这边涉及到计算机相关的知识,忘记的可以补习一下二、八、10、16进制以及原码、反码、补码相关的知识

相关链接:https://blog.csdn.net/asd1358355022/article/details/127898198?spm=1001.2014.3001.5501

1.png

其他运算符

1.png

特别说明(三元运算符)

image.png

1.png

运算符优先级

1.png

键盘输入语句

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。

fmt 包的 fmt.Scanln() 或者 fmt.Scanf()

函数信息

1.png

1.png

代码示例

package main
import "fmt"
func main() {
    var name string
    var age int
    var address string
    //方式1:fmt.Scanln
    fmt.Println("请输入姓名")
    fmt.Scanln(&name)
    fmt.Println("请输入年龄")
    fmt.Scanln(&age)
    fmt.Println("请输入地址")
    fmt.Scanln(&address)
    fmt.Printf("姓名:%v, 年龄:%v, 地址:%v   \n", name, age, address)
    //方式2:fmt.Scanf
    println("----------------------")
    var name_1 string
    var age_1 int
    var address_1 string
    println("请输入你的姓名、年龄、地址,依次按照空格隔开")
    fmt.Scanf("%s %d %s", &name_1, &age_1, &address_1)
    fmt.Printf("姓名:%v, 年龄:%v, 地址:%v   \n", name_1, age_1, address_1)
}

运行结果

请输入姓名
测试
请输入年龄
18
请输入地址
北京朝阳区
姓名:测试, 年龄:18, 地址:北京朝阳区   
----------------------
请输入你的姓名、年龄、地址,依次按照空格隔开
测试 18 北京朝阳区
姓名:测试, 年龄:18, 地址:北京朝阳区   

程序流程控制

在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。分为:顺序控制、分支控制、循环控制。


顺序结构:代码从上往下依次执行

分支结构:根据不同的条件,执行不同的语句

循环结构: 根据指定的条件,重复执行某段代码。


顺序流程

程序从上到下逐行地执行,中间没有任何判断和跳转。

一个案例说明,必须下面的代码中,没有判断,也没有跳转.因此程序按照默认的流程执行,即顺序控制。

如下图所示,程序自上而下执行

image.png

执行流程图

1.png

分支控制

分支语句的作用就是满足判断条件进入代码块中执行相应的功能


if else流程控制

Golang 中规定与if匹配的{必须与if和表达式位于同一行,否则会发生编译错误。同样的,else也必须上一个分支的}位于同一行。表达式两边可以省略()。 分为单分支语句、双分支语句、多分支语句


switch case流程控制

除了if关键值,Golang 中还提供了switch语句对大量的值和表达式进行判断,除了支持数值常量,Golang 的 switch 还能对字符串、表达式等复杂情况进行处理。


switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上到下逐一测

试,直到匹配为止。


匹配项后面也不需要再加 break

**switch 和 if 的比较 **

总结了什么情况下使用 switch ,什么情况下使用 if


如果判断的具体数值不多,而且符合整数、浮点数、字符、字符串这几种类型。建议使用 **swtich **

语句,简洁高效。


其他情况:对区间判断和结果为 **bool **类型的判断,使用 if,if 的使用范围更广。

代码示例

package main
import "fmt"
func main() {
  println("-------if else流程控制------")
  //流程控制
  num := 10
  //单分支语句
  if num <= 10 {
    //满足条件进入流程
    fmt.Println("单分支测试")
  }
  //双分支语句
  if num > 10 {
    fmt.Println("双分支测试01")
  } else {
    fmt.Println("双分支测试02")
  }
  //多分支语句,注意⚠️:双分支语句或者多分支语句执行后只会进入到一个流程当中,代码由上而下执行,只要满足即出流程不会就算是其他分支满足条件也不会进入到分支里
  if num > 10 {
    fmt.Println("多分支测试01")
  } else if num == 10 {
    fmt.Println("多分支测试02")
  } else {
    fmt.Println("多分支测试03")
  }
  //嵌套语句(在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层分 支外面的分支结构称为外层分支。)
  num_01 := 20
  if num > 10 {
    fmt.Println("多分支测试04")
  } else if num == 10 {
    if num_01 > 20 {
      fmt.Println("嵌套分支测试01")
    } else if num_01 == 20 {
      fmt.Println("嵌套分支测试02")
    }
    fmt.Println("分支测试05")
  }
  println("-------switch case流程控制------")
  //switch case流程控制
  currentTemperature := 25
  switch currentTemperature {
  case 10:
    fmt.Println("当前天气凉")
  case 25:
    fmt.Println("当前天气温暖~")
  case 40:
    fmt.Println("当前天气热死了!")
  default:
    //以上条件都不符合才会进入这里
    fmt.Println("这鬼天气多变!")
  }
}

运行结果

如下所示,每一种分支只会在满足条件进入到最先满足条件的分支里执行功能

-------if else流程控制------
单分支测试
双分支测试02
多分支测试02
嵌套分支测试02
分支测试05
-------switch case流程控制------
当前天气温暖~

循环控制

Golang 的循环体仅提供了for关键字,没有其他语言中提供的while或者do-while形式

语法格式

for 循环变量初始化; 循环条件; 循环变量迭代 {

循环操作(语句)

}

package main
import "fmt"
func main() {
  //简单示例,循环10次
  for i := 0; i < 10; i++ {
    fmt.Println("循环次数 i = ", i)
  }
  //上述还可以这样写
  j := 0
  for j < 10 {
    fmt.Println("循环次数 j = ", j)
    j++
  }
}

运行结果

循环次数 i =  0
循环次数 i =  1
循环次数 i =  2
循环次数 i =  3
循环次数 i =  4
循环次数 i =  5
循环次数 i =  6
循环次数 i =  7
循环次数 i =  8
循环次数 i =  9
循环次数 j =  0
循环次数 j =  1
循环次数 j =  2
循环次数 j =  3
循环次数 j =  4
循环次数 j =  5
循环次数 j =  6
循环次数 j =  7
循环次数 j =  8
循环次数 j =  9

跳转控制(break/continue/goto)

break:满足条件终止循环

continue:满足条件跳过此次循环进入下次循环

goto:跳转到指定的标签

Go 语言的 goto 语句可以无条件地转移到程序中指定的行。

goto 语句通常与条件语句配合使用。可用来实现条件转移,跳出循环体等功能。

在 Go 程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序

都产生困难


return:结束次方法或者代码块的运行(言外之意:到这里就可以了,不能继续往下走了)

package main
import "fmt"
func main() {
    println("------break测试-----")
  k := 0
  for { //等价于 for ; ;
    if k >= 10 {
      //退出循环
      break
    }
    fmt.Println("循环次数 k = ", k)
    k++
  }
    /continue测试///
  println("------continue测试-----")
  l := 0
  for l < 10 {
    if l == 2 {
      fmt.Println("我是continue测试, l = ", l)
      l++
      continue
    }
    fmt.Println("循环测试, l = ", l)
    l++
  }
    /goto测试///
  println("------goto测试-----")
  flag := false
  if flag == false {
    goto label_01
  }
  println("------goto测试----1-----")
  println("------goto测试----2-----")
  label_01: //跳转标识,上述输出不会打印
  println("------goto测试----3-----")
  println("------goto测试----4-----")
    /return测试///
  println("------return测试-----")
  returnTestNum := 0
  if returnTestNum == 0 {
    println("-----到此为止了!-----")
    return
  }
  println("-----执行动作1-----")
  println("-----执行动作2-----")
}

运行结果

------break测试-----
循环次数 k =  0
循环次数 k =  1
循环次数 k =  2
循环次数 k =  3
循环次数 k =  4
循环次数 k =  5
循环次数 k =  6
循环次数 k =  7
循环次数 k =  8
循环次数 k =  9
------continue测试-----
循环测试, l =  0
循环测试, l =  1
我是continue测试, l =  2
循环测试, l =  3
循环测试, l =  4
循环测试, l =  5
循环测试, l =  6
循环测试, l =  7
循环测试, l =  8
循环测试, l =  9
------goto测试-----
------goto测试----3-----
------goto测试----4-----
------return测试-----
-----到此为止了!-----

生成随机数

package main
import (
  "fmt"
  "math/rand"
  "time"
)
func main() {
  time.Now().UnixNano() 返回当前系统的纳秒数
  rand.Seed(time.Now().Unix())
  rangerNum := 0
  for i := 0; i < 10; i++ {
    rangerNum = rand.Intn(100) + 1
    fmt.Println(rangerNum)
  }
}

数组

和c语言相同,Go语言也提供了数组类型的数据结构,数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

package main
import (
  "fmt"
)
func main() {
  //创建数组
  var arr_0 [4]int = [4]int{1, 2, 3, 4}
  var arr_1 = [4]float64{111.000, 222, 333, 444.444}
  var arr_2 = [...]bool{false, true, true}      // 自行判断长度,中括号里...一个不能少
  var arr_3 = [...]byte{1: 'a', 0: 'b', 2: 'c'} // 指定索引和值
  fmt.Printf("arr_0= %v, 类型:%T \n", arr_0, arr_0)
  fmt.Printf("arr_1= %v, 类型:%T \n", arr_1, arr_1)
  fmt.Printf("arr_2= %v, 类型:%T \n", arr_2, arr_2)
  fmt.Printf("arr_3= %v, 类型:%T \n", arr_3, arr_3)
}

切片

slice 与 array 接近,但是在新的元素加入的时候可以增加长度。slice 总是指向底层的

一个 array。slice 是一个指向 array 的指针,这是其与 array 不同的地方;slice 是引用

类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array。例 引 用 类 型 使

如,如果一个函数需要一个 slice 参数,在其内对 slice 元素的修改也会体现在函数调 用 make 创建。

用者中,这和传递底层的 array 指针类似。通过:

sl := make([]in t , 10)

创建了一个保存有 10 个元素的 slice。需要注意的是底层的 array 并无不同。slice 总

是与一个固定长度的 array 成对出现。其影响 slice 的容量和长度。

package main
import "fmt"
func main() {
    //数组
  array_0 := [...]int{1, 2, 3, 4}
  //切片(将数组下标为1到3的数据取出来,不含3)
  slice := array_0[1:3]
  fmt.Printf("slice 内容:%v \n", slice)
  fmt.Printf("数组下标为1的数据为:%v, 数组下标为2的数据为:%v \n", array_0[1], array_0[2])
  //通过make创建一个数组,但是过程对我们是不可见的
  sl := make([]int, 5)
  fmt.Printf("切片sl类型:%T, 数据为:%v, 长度:%v \n", sl, sl, len(sl))
  println("-------追加内容测试-------")
  //追加数据到切片中,尾部追加
  sl = append(sl, 1, 2, 3, 3, 4)
  fmt.Printf("尾部追加切片sl类型:%T, 数据为:%v 长度:%v \n", sl, sl, len(sl))
  //追加切片到切片中,尾部追加
  sl = append(sl, []int{1, 2, 3}...)
  fmt.Printf("尾部追加切片后切片sl类型:%T, 数据为:%v 长度:%v \n", sl, sl, len(sl))
  //追加数据到切片中,头部追加
  sl = append([]int{0, 1, 2}, sl...)
  fmt.Printf("头部追加切片sl类型:%T, 数据为:%v 长度:%v \n", sl, sl, len(sl))
  println("-------合并内容测试-------")
  //合并两个切片内容,注意⚠️:切片相同下标的元素会被合并的元素覆盖
  copy(sl, slice)
  fmt.Printf("合并后切片sl类型:%T, 数据为:%v 长度:%v \n", sl, sl, len(sl))
  strArray_1 := [...]string{"123", "234", "456", "999"}
  strArray_2 := [...]string{"111", "222", "333", "888"}
  strSlice_1 := strArray_1[0:4]
  strSlice_2 := strArray_2[1:4]
  fmt.Printf("原有strSlice_1 内容:%v \n", strSlice_1)
  fmt.Printf("原有strSlice_2 内容:%v \n", strSlice_2)
  copy(strSlice_1, strSlice_2)
  fmt.Printf("合并后strSlice_1 内容:%v \n", strSlice_1)
  fmt.Printf("合并后strSlice_2 内容:%v \n", strSlice_2)
  println("-------删除内容测试-------")
  //删除内容
  newSlice := []int{1, 2, 3}
  newSlice = newSlice[1:] //从下标1开始取所有数据,相当于删除了下标为0(即第一个元素)
  fmt.Printf("删除后newSlice 内容:%v \n", newSlice)
  //删除尾部元素
  newSlice = []int{1, 2, 3, 4, 5, 6, 7, 8}
  newSlice = newSlice[0 : len(newSlice)-1] //删除最后一个元素
  fmt.Printf("删除后newSlice 内容:%v \n", newSlice)
  newSlice = newSlice[0 : len(newSlice)-3] //删除最后3个元素
  fmt.Printf("删除后newSlice 内容:%v \n", newSlice)
  //进行略复杂删除,使用append删除
  newArray_01 := [...]string{"a", "b", "c", "d"}
  stringsSlice_01 := append(newArray_01[:2], newArray_01[3]) //删除中间下标为2的元素,公式即append(newArray_01[:i], newArray_01[i + 1]),i= 2,删除多个可以参考
  fmt.Printf("删除后newArray_01 内容:%v \n", stringsSlice_01)
  //进行略复杂删除,使用copy删除
  newArray_02 := [...]string{"e", "f", "g", "h"}
  fmt.Printf("%v, ---, %v \n", newArray_02[:2], newArray_02[3:])
  //看输出内容加以理解,此处将元素["e", "f"] ["h"] 合并后 变为 ->["h", f],可以看下上面切片合并加以理解
  //按照需求切分,此处比较绕,多思考
  copyAfterLen := copy(newArray_02[:2], newArray_02[3:])
  fmt.Printf("copy 后长度:%v newArray_02内容:%v \n", copyAfterLen, newArray_02)
  stringsSlice_02 := newArray_02[:copyAfterLen]
  fmt.Printf("删除后newArray_02 内容:%v \n", stringsSlice_02)
}

运行结果

slice 内容:[2 3] 
数组下标为1的数据为:2, 数组下标为2的数据为:3 
切片sl类型:[]int, 数据为:[0 0 0 0 0], 长度:5 
-------追加内容测试-------
尾部追加切片sl类型:[]int, 数据为:[0 0 0 0 0 1 2 3 3 4] 长度:10 
尾部追加切片后切片sl类型:[]int, 数据为:[0 0 0 0 0 1 2 3 3 4 1 2 3] 长度:13 
头部追加切片sl类型:[]int, 数据为:[0 1 2 0 0 0 0 0 1 2 3 3 4 1 2 3] 长度:16 
-------合并内容测试-------
合并后切片sl类型:[]int, 数据为:[2 3 2 0 0 0 0 0 1 2 3 3 4 1 2 3] 长度:16 
原有strSlice_1 内容:[123 234 456 999] 
原有strSlice_2 内容:[222 333 888] 
合并后strSlice_1 内容:[222 333 888 999] 
合并后strSlice_2 内容:[222 333 888] 
-------删除内容测试-------
删除后newSlice 内容:[2 3] 
删除后newSlice 内容:[1 2 3 4 5 6 7] 
删除后newSlice 内容:[1 2 3 4] 
删除后newArray_01 内容:[a b d] 
[e f], ---, [h] 
copy 后长度:1 newArray_02内容:[h f g h] 
删除后newArray_02 内容:[h] 

映射(map)

在 Go 中有 map 类型。map 可以认为是一个用字符串做索引的数

组(在其最简单的形式下)。

  //初始化一个map,长度为10
  stringStringMap := make(map[string]string, 10)
  //向map增加元素
  stringStringMap["name"] = "Mir Li"
  stringStringMap["age"] = "18"
  fmt.Printf("stringMap 内容:%v 类型:%T, 长度:%d \n", stringStringMap, stringStringMap, len(stringStringMap))
  //覆盖元素操作
  stringStringMap["name"] = "Mir Zhang"
  fmt.Printf("stringMap 内容:%v 类型:%T, 长度:%d \n", stringStringMap, stringStringMap, len(stringStringMap))
  //删除元素操作
  delete(stringStringMap, "age")
  fmt.Printf("stringMap 内容:%v 类型:%T, 长度:%d \n", stringStringMap, stringStringMap, len(stringStringMap))
  //判断是否存在元素
  _, present := stringStringMap["name"]
  fmt.Printf("stringStringMap 元素是否存在%v元素:%v \n", "name", present)
  //其他初始化方式,初始即赋值
  stringIntMap := map[string]int{
    "Jan": 31, "Feb": 28, "Mar": 31,
    "Apr": 30, "May": 31, "Jun": 30,
    "Jul": 31, "Aug": 31, "Sep": 30,
    "Oct": 31, "Nov": 30, "Dec": 31,
  }
  fmt.Printf("stringIntMap 内容:%v 类型:%T, 长度:%d \n", stringIntMap, stringIntMap, len(stringIntMap))

运行内容

stringMap 内容:map[age:18 name:Mir Li] 类型:map[string]string, 长度:2 
stringMap 内容:map[age:18 name:Mir Zhang] 类型:map[string]string, 长度:2 
stringMap 内容:map[name:Mir Zhang] 类型:map[string]string, 长度:1 
stringStringMap 元素是否存在name元素:true 
stringIntMap 内容:map[Apr:30 Aug:31 Dec:31 Feb:28 Jan:31 Jul:31 Jun:30 Mar:31 May:31 Nov:30 Oct:31 Sep:30] 类型:map[string]int, 长度:12 

函数

定义:为完成某一功能的程序指令(语句)的集合,称为函数。

格式

func (p mytype) funcname(q int) (r,s int) { return 0,0 }
}

.0 关键字 func 用于定义一个函数;

.1 函数可以绑定到特定的类型上。这叫做 接收者。有接收者的函数被称作 method。 第 5 章将对其进行说明;

.2 funcname 是你函数的名字;

.3 int 类型的变量 q 作为输入参数。参数用 pass-by-value 方式传递,意味着它们会

被复制;

.4 变量 r 和 s 是这个函数的 命名返回值。在 Go 的函数中可以返回多个值。参阅

第 28 页的 “多值返回”。如果不想对返回的参数命名,只需要提供类型:(int,int)。

如果只有一个返回值,可以省略圆括号。如果函数是一个子过程,并且没有任何

返回值,也可以省略这些内容;

.5 这是函数体。注意 return 是一个语句,所以包裹参数的括号是可选的。


示例代码

package main
import (
  "fmt"
)
func main() {
  returnValue_1, returnValue_2 := testFunction(10)
  //打印返回值
  fmt.Printf("returnValue_1 = %v, returnValue_2 = %v", returnValue_1, returnValue_2)
}
//testFunction:函数名, value:函数入参, b、c:函数执行返回参数
func testFunction(value int) (b int, c int) {
  fmt.Println("value = ", value)
  return 10, 10
}

运行结果:

value =  10
returnValue_1 = 10, returnValue_2 = 10
相关文章
|
17天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
26 7
|
17天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
17天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
92 71
|
16天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
100 67
|
19天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
88 62
|
21天前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
17天前
|
存储 Go
go语言中映射
go语言中映射
32 11
|
19天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
29 12
|
18天前
|
Go 索引
go语言使用索引遍历
go语言使用索引遍历
26 9
|
22天前
|
安全 Serverless Go
Go语言中的并发编程:深入理解与实践####
本文旨在为读者提供一个关于Go语言并发编程的全面指南。我们将从并发的基本概念讲起,逐步深入到Go语言特有的goroutine和channel机制,探讨它们如何简化多线程编程的复杂性。通过实例演示和代码分析,本文将揭示Go语言在处理并发任务时的优势,以及如何在实际项目中高效利用这些特性来提升性能和响应速度。无论你是Go语言的初学者还是有一定经验的开发者,本文都将为你提供有价值的见解和实用的技巧。 ####