golang基础知识

Posted 敢于对过去告一个段落,才有信心掀开新的篇章!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang基础知识相关的知识,希望对你有一定的参考价值。

一 golang基础知识

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种计算机编程语言语言。
设计初衷
Go语言是谷歌推出的一种的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发Go,是因为过去10多年间软件开发的难度令人沮丧。派克表示,和今天的C++或C一样,Go是一种系统语言。他解释道,"使用它可以进行快速开发,同时它还是一个真正的编译语言,我们之所以现在将其开源,原因是我们认为它已经非常有用和强大。"

  • 计算机硬件技术更新频繁,性能提高很快。目前主流的编程语言发展明显落后于硬件,不能合理利用多核多CPU的优势提升软件系统性能。
  • 软件系统复杂度越来越高,维护成本越来越高,目前缺乏一个足够简洁高效的编程语言。
  • 企业运行维护很多c/c++的项目,c/c++程序运行速度虽然很快,但是编译速度确很慢,同时还存在内存泄漏的一系列的困扰需要解决。

应用领域

数据类型

  • int :有符号的整数类型:具体占几个字节要看操作系统的分配:不过至少分配给32位。
  • uint:非负整数类型:具体占几个字节要看操作系统的分配:不过至少分配给32位。
  • int8:有符号的整数类型:占8位bit:1个字节。范围从负的2的8次方到正的2的8次方减1。
  • int16:有符号的整数类型:占16位bit:2个字节。范围从负的2的16次方到正的2的16次方减1。
  • int32:有符号的整数类型:占32位bit:4个字节。范围从负的2的32次方到正的2的32次方减1。
  • int64:有符号的整数类型:占64位bit:8个字节。范围从负的2的64次方到正的2的64次方减1。
  • uint8:无符号的正整数类型:占8位:从0到2的9次方减1.也就是0到255.
  • uint16:无符号的正整数类型:占16位:从0到2的8次方减1.
  • uint32:无符号的正整数类型:占32位:从0到2的32次方减1.
  • uint64:无符号的正整数类型:占64位:从0到2的64次方减1.
  • uintptr:无符号的储存指针位置的类型。也就是所谓的地址类型。
  • rune :等于int32:这里是经常指文字符。
  • byte:等于uint8:这里专门指字节符
  • string:字符串:通常是一个切片类型:数组内部使用rune
  • float32:浮点型:包括正负小数:IEEE-754 32位的集合
  • float64:浮点型:包括正负小数:IEEE-754 64位的集合
  • complex64:复数:实部和虚部是float32
  • complex128:复数:实部和虚部都是float64
  • error:错误类型,真实的类型是一个接口。
  • bool:布尔类型

基础组件分为以下几种

  • 引用类型
    • slice
    • interface
    • chan
    • map
  • 非引用类型
    • array
    • func
    • struct

声明包和引用包

package main

import (
"fmt"
"lind-go/common"
//自定义的本项目的包
_ "lind-go/common"
)

赋值符号

var a
b :=

其中var 这种方式不论是局部还是全局变量都可以使用,但是后者也就是:=只有局部变量可以使用。也就是只有函数内部才能使用。
并且,var后面的变量后面的类型是可以省略的,省略后,go会在编译过程中自动判断。所以如果不省略就是长这样 var a int 。
数组的初始化

// 初始化的方式1
a := [6]string
// 初始化的方式2
var a [6]string

a[0] = "0"
a[1] = "1"
a[2] = "2"
a[3] = "3"
a[4] = "4"
a[5] = "5"

万字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基础知识

学golang之前都需要哪些前置知识?

Golang基础教程

知识分享之Golang——精选的组件库、组件列表,各种golang组件都可找到

golang 特殊知识点

[译]Golang 知识点总结