ITPub博客

首页 > 数据库 > 数据库开发技术 > 周末学【go+微服务+云原生】go极速学(2)-语法篇-变量和容器的一些学习

周末学【go+微服务+云原生】go极速学(2)-语法篇-变量和容器的一些学习

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-10-31 00:00:00 0 删除 编辑


语法这东西基本是每个编程语言必须的吧!

本小节主要内容:

比如:

  • 数据类型
  • 变量类型和定义
  • 变量的什么周期
  • 指针类型变量
  • 常量的定义
  • 容器的对象类型和定义

接下来的小节,会主要是对这些基础的语法的东西最简单的实践笔记.

以下的搬砖总结笔记·,我仅仅是提取我核心需要记录的来记录!

搬砖资料来源:

http://c.biancheng.net/view/21.html

https://www.runoob.com/go

1 GO言语中的数据类型

1.1常见几种数据类型

  • 整型
    • 按长度划分:int8 int16 int32 int64
    • 无符号类型:uint8(byte) uint16(short) uint32(long) uint64(long)
  • 浮点型
    • flooat32、float64
  • 布尔型: true、false
  • 字符串
    • ninu8 可以理解为是就是byte
    • rune 类型的字符串代表一个utf-8字符,通常用于中文的或其他复合的字符处理中、它本质是一个int32类型的对象
  • 字节
  • 切片
  • 数组
  • 结构体
  • 函数
  • map类型
  • channel类型
  • 指针类型(Pointer)

1.2关于变量

变量是一种使用方便的占位符,用于引用计算机内存地址。

1.2.1变量类型分类

  • 指针变量
  • 常量变量
  • 全局变量
  • 局部变量
  • 匿名变量

1.2.1变量声明方式

package main

import "fmt"

func main() {
 //声明的变量一定要被使用!

 //=====================变量的定义=====================
 //  定义字符串类型的变量-并初始化
 var a string = "Runoob"
 fmt.Println(a)
 // 定义整型类型的变量,并初始化,可以同时定义多个
 var b, c int = 1, 2
 fmt.Println(b, c)
 // 没有初始化就为零值
 var e int
 fmt.Println(e)
 // bool 零值为 false
 var f bool
 fmt.Println(f)

 //=====================: =局部变量的定义=====================
 var test = "你好"
 fmt.Println(test)
 //test: = "啥都好说" //经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:
 xiaozhong := 1 // 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
 fmt.Println(xiaozhong)

 //=====================: 多变量的同时的定义=====================
 var vname1, vname2, vname3 = "v1", 2, 3 // 和 python 类型,不需要显示声明类型,它可以自动推断
 fmt.Println(vname1, vname2, vname3)
 //vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误

}

1.2.2 匿名变量(没名字的变量)

  • 匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。
  • 任何赋给这个标识符的值都将被抛弃
  • 匿名变量不占用内存空间,不会分配内存-
  • 匿名变量与匿名变量之间也不会因为多次声明而无法使用。

示例代码:

package main

import "fmt"

//定义一个函数
func GetData() (string, int) {
 return "你好", 2021
}
func main() {
 //返回返回两个值,但是对返回的值进行有所取舍的操作
 a, _ := GetData()
 _, b := GetData()
 fmt.Println(a, b)
}

执行结果:

你好 2021

1.2.3 变量的作用域范围

  • 常量变量

    • 不可被修改的变量,是一个简单值的标识符,在程序运行时不可修改
  • 全局变量:

    • 函数外定义的变量称为全局变量
    • 全局变量可以在整个包甚至外部包(被导出后)使用
    • 它的生命周期和整个程序的运行周期是一致的;
  • 局部变量:

    • 函数内定义的变量称为局部变量
    • 用域只在函数体内,参数和返回值变量也是局部变量
    • 它的生命周期则是动态的,从创建这个变量的声明语句开始,到这个变量【不再被引用为止】;
  • 形式参数:

    • 函数定义中的变量称为形式参数
    • 形式参数会作为函数的局部变量来使用
    • 它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁。

2 变量类型的中--指针类型

一些概念的理解:

  • 变量是一种使用方便的占位符,用于引用计算机内存地址。

  • 变量的内存地址的获取方法是:& 变量值

指针概念中的两个核心概念点:

  • 类型指针
    • 它允许对这个指针类型的数据进行修改传递数据的使用指针,而无须拷贝数据。但是它进行偏移和运算。
    • 不会发生指针偏移,从而避免了非法修改关键性数据的问题
    • 同时,垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。
  • 切片
    • 由指向其实元素的原始指针和元素梳理和容量组成。
    • 切片比原始指针具备更强大的特性,而且更为安全-
    • 切片在发生越界时,运行时会报出宕机,并打出堆栈
    • 原始指针异常的时候只会崩溃。

理解指针三个要素概念

  • 指针地址
  • 指针类型
  • 指针取值

梳理理解点:

  • 每个变量在【运行时】都拥有一个地址,这个地址代表变量在【内存中的位置】
  • 获取每个变量的地址方式:在变量名前面添加&操作符(前缀)来获取变量的内存地址(取地址操作) 如下示例代码中:
    ptr := &v    // v 的类型为 T,任意的变量的类型
    其中 v 代表被取地址的变量,变量 v 的地址使用【变量 ptr 】进行接收,ptr 的类型为【*T】,称做 【T 的指针类型】,*代表指针。

总结:变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。

白话理解:

ptr := &v    
1: &v 返回的是V变量的指针地址(内存地址)
2:ptr 保存了V变量的指针地址(内存地址)的地址
3:ptr这个变量的类型叫做 v变量的指针类型
4:*ptr 通过【*指针类型变量】可以获取到变量v原来的值(不是内存地址)

示例代码1:

package main

import (
 "fmt"
)

func main() {
 //声明整型变量 cat。
 var cat int = 1
 //声明字符串变量 str。
 var str string = "banana"
 //使用 fmt.Printf 的动词%p打印 cat 和 str 变量的内存地址,指针的值是带有0x十六进制前缀的一组数据。
 fmt.Printf("%p %p", &cat, &str)
}

执行结果:

指针的值是带有0x十六进制前缀的一组数据。

示例代码2:

使用&操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用操作符,也就是指针取值,代码如下:取地址操作符&和取值操作符是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

使用new函数创建指针

# new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。
str := new(string)
*str = "Go语言教程"
fmt.Println(*str)
// 输出 Go语言教程

3:变量中的--栈(Stack)和堆(heap)

  • 栈(Stack)定义:是一种比较特殊线性表的数据结构,可以用于内存的分配。

  • 堆(heap)定义:也是一种数据结构,用于存放进程执行中被动态分配的内存段。

  • 栈(Stack)的特点:

    • 先进后出(LIFO)
    • 手枪弹夹式的结构
    • 通过栈(Stack)分配的内存和回收速度快
    • 通常是用来存放程序暂时创建的局部变量,也就是我们函数的大括号{ }中定义的局部变量
  • 堆(heap)的特点:

    • 它的大小并不固定,可动态扩张或缩减。
    • 合适分配不可预知大小的内存,单也因此有速度慢的问题,且会有内存碎片,不完全使用一分配的内存空间。

4:变量逃逸的理解。

其实这个词我也不太明白是啥,后来百度一番理解收,其实总结下来就是:

这个所谓的变量逃逸其实糗事,让程序的自动化决定变量的分配内存的方式、是分配堆内存黑栈内存,由GO编译器自动化进行完成选择,开发者就不用纠结这个分配的方式的问题。

编译器觉得变量应该分配在堆和栈上的原则是:

  • 变量是否被取地址;
  • 变量是否发生逃逸。

5:枚举常量

常量

  • 使用关键字 const 定义
  • 用于存储不会改变的数据
  • 常量是在编译时被创建的,即使定义在函数内部也是如此,
  • 只能是布尔型、数字型(整数型、浮点型和复数)和字符串型。

iota 常量生成器

iota 常量生成器(这个用着估计比较难理解,有些地方需要多用才知道!)

  • 它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。
  • 每一行都会递增
  • 如果遇到 iota 则复用上一行
  • 有设置特点的值则会直接设置

6 常见容器数据类型

‘容器’:是对数据的存贮和祖师的数据的一种方式的统称。常见的类型:

  • 数组
  • 切片(slice)
  • 映射(map)
  • 列表(list)

6.1数组

定义:

  • 定长的固定大小的连续空间,内部的元素可修改,但是长度的大小不可变。
  • 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

声明方式:

Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:

var variable_name [SIZE] variable_type

初始化数组:

以下演示了数组初始化:

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或用 ... 代替数组的长度
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

数据的遍历:

package main

import (
 "fmt"
)

func main() {
 // 声明数组的同时快速初始化数组
 balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

 /* 输出数组元素 */
 for i := 0; i < 5; i++ {
  fmt.Printf("balance[%d] = %f\n", i, balance[i])
 }
 fmt.Printf("==============")
 for k, v := range balance {
  fmt.Printf("balance[%d] = %f\n", k, v)
 }

 // 输出 Go语言教程
}

6.2切片

定义:

  • 切片是对数组的抽象。
  • 切片又称是一种("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
  • 切片不需要说明长度。

其实和py切片差不多!

切片声明:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

初始化:

切片初始化
s :=[] int {1,2,3 } 
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。

切片操作

  • 表示原来的切片数据s[:]:
s :=[] int {1,2,3 } 
s[:]
  • 切片重置和清空
s :=[] int {1,2,3 } 
s[0:0]
  • 切片的元素添加
package main

import (
 "fmt"
)

func main() {
 // 声明数组的同时快速初始化数组
 s := []int{1, 2, 3}
 s = append(s, 0)
 fmt.Printf(" %d\n", s)
 // 输出  [1 2 3 0]
}

切片的删除:

package main

import (
 "fmt"
)

func main() {
 // 声明数组的同时快速初始化数组
 s := []int{1, 2, 3}
 s = append(s, 0)
 fmt.Printf(" %d\n", s)
 // 输出  [1 2 3 0]

 //删除切片的指定的位置
 del_index := 1
 s = append(s[:del_index], s[del_index+1:]...)

 fmt.Printf(" %d\n", s)
}

注意点:

  • 1:一个切片在未初始化之前默认为 nil,长度为 0
  • 2:可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]
  • 3:切片的删除没有提供声明语法糖,需要使用的切片原来的特性来解决处理,如果对于大数据的额容器的对象,需要频繁进行移除的时候,需要慎重考虑!

6.3 映射关系容器map (PY中字典)

完全来自:https://www.runoob.com/go/go-map.html

我的主要是执行一次代码而已!

定义:

  • Map 是一种无序的键值对的集合
  • 通过 key 来快速检索数据,key 类似于索引,指向数据的值。
  • 是一种集合,所以我们可以像迭代数组和切片那样迭代它

PS:Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的

声明:

  • 内建函数 make
  • 也可以使用 map 关键字来定义 Map:
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

示例:

package main

import "fmt"

func main() {
    var countryCapitalMap map[string]string /*创建集合 */
    countryCapitalMap = make(map[string]string)

    /* map插入key - value对,各个国家对应的首都 */
    countryCapitalMap [ "France" ] = "巴黎"
    countryCapitalMap [ "Italy" ] = "罗马"
    countryCapitalMap [ "Japan" ] = "东京"
    countryCapitalMap [ "India " ] = "新德里"

    /*使用键输出地图值 */ 
    for country := range countryCapitalMap {
        fmt.Println(country, "首都是", countryCapitalMap [country])
    }

    /*查看元素在集合中是否存在 */
    capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */
    /*fmt.Println(capital) */
    /*fmt.Println(ok) */
    if (ok) {
        fmt.Println("American 的首都是", capital)
    } else {
        fmt.Println("American 的首都不存在")
    }
}

输出结果:

France 首都是 巴黎
Italy 首都是 罗马
Japan 首都是 东京
India  首都是 新德里
American 的首都不存在

元素删除:

package main

import "fmt"

func main() {
 var countryCapitalMap map[string]string /*创建集合 */
 countryCapitalMap = make(map[string]string)

 /* map插入key - value对,各个国家对应的首都 */
 countryCapitalMap["France"] = "巴黎"
 countryCapitalMap["Italy"] = "罗马"
 countryCapitalMap["Japan"] = "东京"
 countryCapitalMap["India "] = "新德里"

 /*使用键输出地图值 */
 for country := range countryCapitalMap {
  fmt.Println(country, "首都是", countryCapitalMap[country])
 }

 /*查看元素在集合中是否存在 */
 capital, ok := countryCapitalMap["American"] /*如果确定是真实的,则存在,否则不存在 */
 /*fmt.Println(capital) */
 /*fmt.Println(ok) */
 if ok {
  fmt.Println("American 的首都是", capital)
 } else {
  fmt.Println("American 的首都不存在")
 }

 /*删除元素*/
 delete(countryCapitalMap, "France")
 fmt.Println("法国条目被删除")

 fmt.Println("删除元素后地图")

 /*打印地图*/
 for country := range countryCapitalMap {
  fmt.Println(country, "首都是", countryCapitalMap[country])
 }
}

map的清空

申请的GO竟然没提供方法~!清空的map的方法,唯一办法竟然是重新make一个新的map!

需要考虑一点是map并发安全性问题

这个后续学习到sync.map的时候再去了解一下。

6.4 列表(list)

定义:

  • 列表是一种非连续的存储容器
  • 由多个节点组成,节点通过一些变量记录彼此之间的关系
  • 使用 container/list 包来实现,内部的实现原理是双链表,列表能够高效地进行任意位置的元素插入和删除操作。

实现方法:

  • 单链表、
  • 双链表等。

声明:

1) 通过 container/list 包的 New() 函数初始化 list
变量名 := list.New()

2) 通过 var 关键字声明初始化 list
var 变量名 list.List

初始化和使用

双链表支持从队列前方或后方插入元素,分别对应的方法是 PushFront 和 PushBack。

l := list.New()
l.PushBack("fist")
l.PushFront(67)

列表遍历

遍历双链表需要配合 Front() 函数获取头元素,遍历时只要元素不为空就可以继续进行,每一次遍历都会调用元素的 Next() 函数,代码如下所示:

package main

import (
 "container/list"
 "fmt"
)

func main() {
 l := list.New()
 // 尾部添加
 l.PushBack("canon")
 // 头部添加
 l.PushFront(67)
 for i := l.Front(); i != nil; i = i.Next() {
  fmt.Println(i.Value)
 }
}

结尾

END

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

公众号:微信搜【小儿来一壶枸杞酒泡茶】

小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822

  • 1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。
  • 2:关于文章内容,部分内容参考自互联网整理,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111703