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

文章目录

Golang概述

Go语言三大牛

在这里插入图片描述

谷歌创造Golang的原因

在这里插入图片描述

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 动态语言的快速开发。

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

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

举一个案例(体验):
//go 语言的指针的使用特点(体验)
func testPtr(num *int) {
*num = 20
}

  1. 引入包的概念,用于组织程序结构,Go 语言的一个文件都要归属于一个包,而不能单独存在。
package main //一个文件都要归属于一个包

import "fmt"

func main() {
	fmt.Println("hello word")
}
  1. 垃圾回收机制,内存自动回收,不需开发人员管理
  2. 天然并发 (重要特点)
    1. 从语言层面支持并发,实现简单
    2. goroutine,轻量级线程,可实现大并发处理,高效利用多核。
    3. 基于 CPS 并发模型(Communicating Sequential Processes )实现
  3. 吸收了管道通信机制,形成 Go 语言特有的管道 channel 通过管道 channel , 可以实现不同的 goroute

之间的相互通信。

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

//写一个函数,实现同时返回 和,差
//go 函数支持返回多个值
func getSumAndSub(n1 int, n2 int) (int, int ) {
sum := n1 + n2 //go 语句后面不要带分号.
sub := n1 - n2
return sum , sub
}

  1. 新的创新:比如切片 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的路径就可以了

在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
之后写个hello word

package main //一个文件都要归属于一个包

import "fmt"

func main() {
	fmt.Println("hello word")
}

运行

在这里插入图片描述

Go 程序开发的注意事项

  1. Go 源文件以 “go” 为扩展名。
  2. Go 应用程序的执行入口是 main()函数。 这个是和其它编程语言(比如 java/c)
  3. Go 语言严格区分大小写。
  4. Go 方法由一条条语句构成,每个语句后不需要分号(Go 语言会在每行后自动加分号),这也体现出 Golang 的简洁性。
  5. Go 编译器是一行行进行编译的,因此我们一行就写一条语句,不能把多条语句写在同一个,否

则报错

  1. go 语言定义的变量或者 import 的包如果没有使用到,代码不能编译通过。
  2. 大括号都是成对出现的,缺一不可。

官方参考文档

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 小明

数据类型

在这里插入图片描述

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

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

有符号: 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 
指针相关图示

在这里插入图片描述

标识符

概念

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

标识符的命名规则
  1. 由 26 个英文字母大小写,0-9 ,_ 组成
  2. 数字不可以开头。var num int //ok var 3num int //error
  3. Golang 中严格区分大小写。
  4. 在 golang 中,num 和 Num 是两个不同的变量 (var num int

,var Num int )

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

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

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

在这里插入图片描述

预留关键字

在这里插入图片描述

运算符

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

算数运算符一览表

在这里插入图片描述
在这里插入图片描述

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. 关系运算符的结果都是 bool 型,也就是要么是 true,要么是 false。
  2. 关系运算符组成的表达式,我们称为关系表达式: a > b
  3. 比较运算符"=="不能误写成 “=” !!
逻辑运算符

用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个 bool 值
在这里插入图片描述

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")
	}

  }
赋值运算符

在这里插入图片描述
在这里插入图片描述

位运算符

位运算符这边涉及到计算机相关的知识,忘记的可以补习一下二、八、10、16进制以及原码、反码、补码相关的知识
相关链接:https://blog.csdn.net/asd1358355022/article/details/127898198?spm=1001.2014.3001.5501

在这里插入图片描述

其他运算符

在这里插入图片描述

特别说明(三元运算符)

在这里插入图片描述
在这里插入图片描述

运算符优先级

在这里插入图片描述

键盘输入语句

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

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

函数信息
在这里插入图片描述
在这里插入图片描述

代码示例
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, 地址:北京朝阳区   


程序流程控制

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

  • 顺序结构:代码从上往下依次执行
  • 分支结构:根据不同的条件,执行不同的语句
  • 循环结构: 根据指定的条件,重复执行某段代码。
顺序流程

程序从上到下逐行地执行,中间没有任何判断和跳转。
一个案例说明,必须下面的代码中,没有判断,也没有跳转.因此程序按照默认的流程执行,即顺序控制。
如下图所示,程序自上而下执行
在这里插入图片描述

执行流程图
在这里插入图片描述

分支控制

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

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

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

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

试,直到匹配为止。

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

**switch 和 if 的比较 **
总结了什么情况下使用 switch ,什么情况下使用 if

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

语句,简洁高效。

  1. 其他情况:对区间判断和结果为 **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

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

力不竭!!!战不止!!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值