万字Golang基础知识(肝爆三天三夜,手撕Golang基本语法结构)
Posted 友人苏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了万字Golang基础知识(肝爆三天三夜,手撕Golang基本语法结构)相关的知识,希望对你有一定的参考价值。
Golang基础知识
1初识Golang
首先让我们问候一下世界
package main
import "fmt"
func main(){
fmt.Println("hello, world!\\n")
}
运行结果:
对世界进行了一波问候后,我们对这个简单的程序进行解读分析
(1)第一行我们定义了一个名为 package 的包,main是可执行程序的包名,所有的Go源程序文件头部必须有一个包声明语句。
(2)然后用 关键字 import 导入了一个“fmt”文件,fmt 是 format 的缩写,是一个标准的包,有点类似C语言中的头文件。
(3)关键字 func 声明定义了一个函数, 函数名为main, 在Go语言中 main 代表一个程序的入口,没有 main 函数的程序就像与一间没有没有门的密室,在C语言中也是如此。
(4)main函数里面调用了 fmt 包里的 Println 函数, 其中 “Hello, world!” 是一个字符串常量,而 \\n 是一个转义字符, 换行。
1.1 Go的语法要求
1.1.1 token
token 是构成源程序的基本不可再分割的单元,编译器的编译源程序的第一步就是将源程序分割为一个个独立的 token,这就是语法分析的过程,而token 又可以分为关键字,标识符,操作符、分隔符和字面常量等。
如图所示:
其中操作符本身就是一个天然的分隔符,同时其自身也是一个 token。
纯分隔符本身布局有任何语法意义,只是作为其他 token 的分割功能。例如空格、字符表、换行符等。
从1.2中的代码可以分析出
关键字:
package import func
标识符:
main fmt Println
字面量:
“fmt” “hello, world!\\n”
操作符:
( ) { } .
一个Golang源程序的基本就是由各种 token 构成。
基本框架:
package main
import (
"...."
)
func main(){
....
}
1.2 变量和常量
变量
变量的理解很简单,如其名,可以变化的量,其官方定义是指没有固定的值,可以改变的数。 通常在写程序时都需要用到各中数据,而变量能够方便程序员对内存数据进行访问。
1.变量的完整声明
var varName dataType = value
var a int = 10
var a int = 3 * 5
var a int = b
1.短类型声明
varName := value
//注意:
//:= 声明只能出现在函数内
//Go编译器会自动进行数据类型推断
变量名的命名规则
开头字符必须是字母或下划线,后面可跟多个字符,数字或下划线。
常量
常量”的广义概念是:‘不变化的量’(例如:在计算机程序运行时,不会被程序修改的量。在Golang中常量使用一个名称来绑定一块内存地址,且该内存地址里面存放的内容不可改变。
//类似枚举
package main
import "fmt"
const(
c0 = 2 << iota
c1
c2
c3
c4
)
func main(){
fmt.Println(c0, c1, c2, c3, c4)//结果:2 4 8 16 32
}
还有字符串常量,例如:
a := "hello world"
1.3 基本数据类型
Golang 内置有七类基本数据类型
布尔类型:bool
整形:byte int int8 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
浮点型:float32 float64
复数:complex64 complex128
字符:rune
字符串:string
1.3.1 布尔类型
布尔类型只有两个值,true 和 false,true 代表真,false 代表假,是Go内置的两个预声明标识符。
var a bool
a = true
//or
a := false
//布尔类型数据和整数不能进行相互转换
var a bool
a = 1 //error 1是整型字面量
//逻辑比较判断表达式的返回值都是布尔类型数据
//条件成立的返回值为true,否则返回false
x := 2
y := 1
var b bool = x > y //b = true
var b bool = (x < 0) && (y > 0) // b = false
//if 和 for 语句中的条件部分的返回值也是bool类型
if a < b{
print(b)
}else{
printf(a)
}
for ; true; {//相当于C中的while(1)
}
//声明的bool类型变量如果没有进行初始化,则默认值为false
var c bool // c is false
1.3.2 整型
整型变量的定义方式:
//标准型
var a int
a = 1
//缩减型
a := 1
Go中内置了12种整型类型,byte int int6 int8 int32 int64 uint uint8 uint16 uint32 uint64 uintptr,每种类型可容纳的数据大小不同,并且不同整型相互赋值需要强制转换
var a int = 1
var b int32 = 2
b = a //error
b = (int32)a //true
1.3.3 浮点型
浮点型变量用于表示存储包含小数的数据空间,Go中有 float32 和 float64 两种。
注意事项:
(1)浮点数字面量被默认为 float64 类型
var f = 1.00
(2)在学习C语言时,用浮点型数据进行计算经常出现小数点后面的数据有一些细微的差别,这是与精度丢失有关,计算机很难进行浮点数的精确表示和存储,因此两个浮点数之间不能进行 == 或 != 判断操作。
1.3.4 复数类型
Golang 内置的复数形式有两种,分别是 complex64 和 complex128,复数在计算机中使用两个浮点数表示,一个表示实部,一个表示虚部。complex64 是两个 float32 构成的,complex128 是由两个float64 构成的。表示方法和数学表示法一样。
var value1 complex64 = 3.1 + 5.1
value2 := 3.1 + 6i
//Go 有三个内置函数处理复数
var v = complex(2.1, 3) //构造一个复数
a := real(v) //返回复数实部
b := image(v) //返回复数虚部
1.3.5 字符串
基本介绍:
字符串就是一串固定长度的字符连接起来的字符序列, Go 的字符串是由单个字节连接起来的,Golang 的字符串字节使用 UTF-8 编码标识 Unicode 文本。
案例演示:
package main
import "fmt"
func main(){
//演示 Golang 中 string 的基本使用
var address string = "上海黄浦区 1203"
fmt.Println(address)
}
运行结果:
字符串类型:string
注意事项和使用细节:
1.Golang 的字符串字节使用 UTF-8 编码标识 Unicode 文本, 这样 golang 统一使用 UTF-8 编码,中文乱码问题就不会困扰程序员了。
2.字符串一旦赋值就不可更改了,在 Go 中字符串是不可变的。
//字符串一旦赋值就不可更改了,在 Go 中字符串是不可变的
var str = "hello"
str[0] = 'a'
运行:
3.字符串的表示形式
(1)双引号,会识别转义字符
(2)反引号,以字符串的原生形态输出,包括换行和特殊字符,可以实现防止输出、攻击源代码等效果。
str2 := "abc\\nadd"
fmt.Println(str2)
结果:
反引 ``
str := `
package main
import "fmt"
func main(){
//演示 Golang 中 string 的基本使用
// var address string = "上海黄浦区 1203"
// fmt.Println(address)
//字符串一旦赋值就不可更改了,在 Go 中字符串是不可变的
// var str = "hello"
// str[0] = 'a'
// str2 := "abc\\nadd"
// fmt.Println(str2)
}`
fmt.Println(str)
运行:
字符串的拼接方式
str := "hello" + "world"
str += "hehe"
运行:
4.基本数据转换string
方式一:使用 fmt包的方法
案例说明:
package main
import (
"fmt"
)
func main(){
//使用第一中方式来转换 fmt.Sprintf方法
var num1 int = 99
var str string //void str
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type is %T, str = %q\\n", str, str)
//输出:str type is string, str = "99"
var num2 float64 = 120.345
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type is %T, str = %q\\n", str, str)
//输出:str type is string, str = "120.345000"
var b bool = true
str = fmt.Sprintf("%t", b)
fmt.Printf("str type is %T, str = %q\\n", str, str)
//输出:str type is string, str = "true"
}
方式二:使用 strconv 包的方法
案例说明
//第二种方式:使用 strconv 函数
//func FormatInt(i int64, base int) string
//返回i的base进制的字符串表示。base 必须在2到36之间,
//结果中会使用小写字母'a'到'z'表示大于10的数字。
var num3 int = 16
var str string
str = strconv.FormatInt(int64(num3), 10)
fmt.Printf("str type is %T, str = %q\\n", str, str)
//输出:str type is string, str = "16"
1.3.6 rune 类型
GO 内置的两种字符类型:一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。另一种
是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。
rune 类型等价于 int32 类型。
1.4 复合数据类型
1.4.1 指针
对于指针的理解,我个人认为并没有太大的困难,在学习C的时候,自从我学了指针和动态内存分配后,就再也没用过数组,并且还觉得用数组很lwo(如有冒犯,请保住 -_- 狗头),我个人认为指针是真的好用,非常的naice,指针的理解也很简单,指针和其他类型一样,也是一种变量,他是它是用来存储地址的变量,不同类型的地址对应不同类型的指针
图解:
Golang 中指针的特点:
(1)结构体指针访问结构体字段使用 “ . ” 点操作符, 没有“->”操作符
例如:
package main
import "fmt"
type persion struct{
name string
age int
}
func main(){
//方式1
//方式2
p2 := persion{"张山", 20}
fmt.Println(p2)
//方式3-&
var p3 *persion = new(persion)
//因为p3是指针,因此标准的给给字段赋值方式
(*p3).name = "李四" //也可以写成p3.name = "李四"
(*p3).age = 8439 //原因:go的设计者为了程序员方便,底层会对 p3.name = "李四" 进行处理
fmt.Println(*p3) //会给 p3 加上取值运算符 (*p3).name = "李四"
//方式4-{}
var p4 *persion = &persion{}//也可以 var p4 *persion = &persion{"suyue", 23}
//因为 p4 是一个指针,因此标准的访问字段的方法
//(*p4).name = ...
//也可以和上面一样使用
(*p4).name = "suyue"
(*p4).age = 23
fmt.Println(*p4)
}
(3)Go不支持指针运算(这点令我有点难受,可能C用的太习惯了)
(4)函数中允许返回局部变量的地址。
Go编译器使用“栈逃逸”机制将这种局部变量的空间分配在堆上。例如:
func add(a int, b int) *int{
sum := a + b
return &sun
}
1.4.2 数组
1.数组的定义
var name [n]elemetType
var arr [100]int //声明了一个有100个整型数据的数组
array := []float64{1.10, 4.54, 5.45, 9.21}
2.数组的初始化
arr := [4]int{1, 2, 3, 4}//即指定了长度有初始化了字面量
brr := []int{1, 2, 4, 5}//不指定长度,通过初始化的数量来定义数组的长度
crr := [3]int{1 : 1, 2 : 4}//指定长度,并通过索引值进行初始化
drr := []int{1 : 1, 2 : 3} //不知道总长度,通过索引值进行初始化
1.4.3 切片
切片的基本介绍
(1)切片的英文名时slice。
(2)切片是对数据的引用,因此切片是一个引用类型,在进行传递时,它遵守引用传递的机制。
(3)切片的长度是可变的,因此切片可以理解为一个长度可变的数组(有点类似于C中的动态内存分配)
(4)切片的标准定义
var sliceName []T
var a []int
切片的基本使用
package main
import (
"fmt"
)
func main(){
//方法一:使用数组构造
var intArr = [...]int{1, 5, 4, 8}
slice := intArr[1 : 4] //对intArr从第二个元素
//开始到第三个元素结束
fmt.Println(slice) //[5 4 8]
//使用内置函数make创建切片
a := make([]int, 5, 10) //make(type, len, cap)
fmt.Println("a 的元素: ",a) //[0, 0, 0, 0, 0]
fmt.Printf("a 的长度len = %d\\n", len(a))//len = 5
fmt.Printf("a 的容量cap = %d\\n", cap(a))// cap = 10
//注意:直接声明切片变量时没有意义的
//例如
var b []int
fmt.Printf("%v\\n", b) //结果为 []
}
切片的内存分布
我们还能可以以结构体的形式理解:
type Slice struct{
array *int
len int
cap int
}
切片支持的操作:
(1)内置函数len()返回切片的长度(注意这里的长度只计算已初始化的部分)
(2)内置函数cap()返回切片的容量(容量指的是该切片所能存放数据的最大长度)
(3)内置函数append()对切片进行元素追加
(4)内置函数copy()用于切片的拷贝
a := [...] int {2, 4, 2, 6, 5, 9, 10}
b := make([]int, 5, 10)
c := a[2 : 4]
fmt.Printf("a的元素%v, 长度len=%d,容量cap=%d\\n", a, len(a), cap(a))
fmt.Printf("b的元素%v\\n", b)
b = append(b, 2)
b = append(b, c...)
fmt.Printf("b追加后,元素有%v\\n", b)//[0 0 0 0 0 2 2 6]
d := make([]int, 2, 10)
fmt.Println(d)//[0 0]
copy(d, c)
fmt.Println(d)//[2 6]
1.4.4 map
(1)map的基本概念
map 是 key-value 数据结构,由称字段或者关联数组。类似其他编程语言的集合,在编程中经常用到
对于 map 的理解,我认为可以将其与数组相比较,map的类型格式是:map[K]T,其中K指的是key的数据类型,T指的是valueType,数组是通过下标来对每个数据元素进行访问的,而下标明显都是一个整型的数据类型,而每个下标都对应着数组的一块类型空间,也可以看成是一个 intKey->T 的键值对,而数组下表的使用则是有语言的设计者设计好的,[ ] 中只能用整型的数据,当然有的语言也可通过字母对应的ASCLL吗值对数组进行访问,如C,而 map 却可以让我们自己设定一个数组的下标类型,例如:
package main
import "fmt"
func main(){
ma := map[string]int{"adv" : 1, "bffv" : 2}
fmt.Println(ma["adv"]) //"adv"->1
fmt.Println(ma["bffv"])//"bffv"->2
}
其中,要注意 slice map 和 func 类型不能作为 key 的类型
(2)map的创建
package main
import "fmt"
func main(){
//(1)map的创建
//使用字面量创建
ma := map[string]int{"a" : 1, "b" : 2}
fmt.Println(ma["a"])
fmt.Println(ma["b"])
//使用内置的make函数创建
mp1 := make(map[int]string)
mp2 := make(map[int]string, 10)
mp1[1] = "tom"
mp2[1] = "pony"
fmt.Println(mp1[1])
fmt.Println(mp2[1])
}
运行:
map 在某一方面还有了一点指针的味道 例如map[K]map[K]T,对于这种使用,我们那段代码来体验一下:
package main
import "fmt"
func modifUser(user map[string]map[string]string, name string){
//判断是否由此用户名
if user[name] != nil{
//如果有,则初始化passworld
user[name]["password"] = "you are T"
}else{
user[name] = make(map[string]string, 3)
user[name]["passworld"] = "232332323"
user[name]["nickname"] = "昵称:" + name
}
}
func main(){
var user map[string]map[string]string
user = make(map[string]map[string]string, 10)
//注意要在对 user["sms"] 进行make, 因为 "sms" 对应的值还是 map 类型
user["sms"] = make(map[string]string, 3)
user["sms"]["pawssworld"] = "dsafad"
user["sms"]["nickname"] = "sms"
fmt.Println(user)
modifUser(user, "sms")
modifUser(user, "yrs")
fmt.Println(user)
}
运行:
(3)map 支持的操作
1.map 的单个键值访问格式为 mapName[keyName],更新某个 key 的值时,mapName[keyName] 放到等号左边,访问某个值时放到等号右边。
2.可以使用 range 遍历一个 map 类型,但不能保证每次迭代元素的顺序,例:
package main
import "fmt"
func main(){
mp := make(map[int]string)
mp[1] = "xzy"
mp[2] = "yrs"
mp[3] = "sy"
mp[4] = "syj"
for _, v := range mp{
fmt.Println(v)
}
}
运行:
同样的数据每次输出的结果顺序却不一样。
3.删除 map 中的某个键值,可使用 delet ,语法结构是:delete(mapName, keyName)。delet 是内置函数。
delete(mp, 2)
fmt.Println(len(mp)) //len = 3
1.4.5 struct
首先我们来说一下声明是结构体,结构体其实就是将一个事物的一些或所有信息提取出来,形成的一个新的变量,我们称为结构提变量,所以结构体也可以说是一种自定义的变量。
我们以一个学生为例,学生身上的信息有很多,例如:姓名、年龄、学号、性别、所在院系、身高、体重等等,我们可以将这些信息组合在一起,构建一个名为 student 的结构体变量,那我们就选取姓名
以上是关于万字Golang基础知识(肝爆三天三夜,手撕Golang基本语法结构)的主要内容,如果未能解决你的问题,请参考以下文章
1小时0基础带你Git入门,保姆式教学,万字肝爆! 建议收藏~
❤️肝爆六万字 + 各种图解案例《大数据ETL开发之Kettle工具》小白保姆级教程❤️建议收藏