问道Golang,6月龄必知必会(二)
在我看来,golnag有许多反直观的设计,而且这些设计通常不能自圆其说,导致gohper一而再再而三的调入陷阱。
网上也有很多gohper总结了一些笔记,我再提炼精简一下,挂在脑图树上便于记忆。
值类型包括:所有integer、所有float、bool、string、数组和structure
引用类型包括:指针、slice、map、chan、interface
1. map需要先先初始化,才能使用
issue: 定义、不初始化使用,运行时panic;
solution:make关键字初始化或者使用map字面量
m := make(map[string]float64, 5)
m["pi"] = 3.14
mm := map[string]float64{"pi": 3.14, "pi1": 4.14} // map字面量
slice不需要初始化就可以被append.
var ss []int
ss = append(ss, 1, 2, 3)
fmt.Println(ss) // [1,2,3]
2. map中取得不存在的键,会返回零值,
issue:
func main() {
x := map[string]string{"one":"a","three":"c"}
if v := x["two"]; v == "" { // 不存在该键值对,也能返回零值“”
fmt.Println("true")
}
}
solution: 使用map取值的参数2 bool值来判断
func main() {
x := map[string]string{"one":"a","two":"","three":"c"}
if _,ok := x["two"]; !ok {
fmt.Println("no entry")
}
}
3. array作为函数实参, array内容不受函数内影响
issue: golang array 是值类型,形参是实参的内存拷贝
package main
import "fmt"
func changeFunc(arr [3]int) {
arr[0] = 222
}
func main() {
var arr [3]int = [3]int{1, 2, 3}
changeFunc(arr)
for i, item := range arr {
fmt.Printf("index : %d, item: %d \n\r", i, item)
}
}
solution: 实参从array改成引用类型的 slice。
slice切片的实质是SliceHeader 结构体,值传递slice时,正好将底层数组指针拷贝。
type SliceHeader struct {
Data uintptr // 底层数组的指针
Len int
Cap int
}
4. 变量遮蔽
issue: 块内声明的变量会遮蔽上层的同名变量n
func main() {
n := 0
if true {
n := 1
n++
}
fmt.Println(n) // 0
}
solution: 不要使用同名遮蔽变量,使用 n=1
5. 不可修改的字符串
issue:同大多数语言一样,golang的string是不可变的
func main() {
s := "hello"
s[0] = 'H'
}
# command-line-arguments
.\main.go:8:2: cannot assign to s[0] (value of type byte)
solution: 尝试通过byte/rune 中转
s := "hello"
buf := []rune(s)
buf[0] = 'H'
ss := string(buf)
fmt.Println(ss) // Hello
6. strings.TrimRight 并非剔除右侧后缀
issue: strings.TrimRight实际是将cutset字符串拆成字符,然后原字符串从右向左,直到遇到没有在cutset中国出现的字符。
fmt.Println(strings.TrimRight("ABBA", "BA")) // ""
fmt.Println(strings.TrimRight("ABBAABABCABAB", "BA")) // "ABBAABABC"
solution: 常规的移除后缀使用 strings.TrimSuffix
7. copy函数:最小化拷贝
issue: 内置函数Copyreturns the number of elements copied, which will be the minimum of len(src) and len(dst).
src := []int{1, 2, 3}
var dst []int // 此时len(dst) =0
copy(dst, src)
fmt.Println(dst) // []
solution: 初始化足够空间的dst
src := []int{1, 2, 3}
var dst []int = make([]int, 3)
copy(dst, src)
fmt.Println(dst) // [1,2,3]
或者使用append方法
src := []int{1, 2, 3}
var dst1 []int
dst1 = append(dst1, src...)
fmt.Println(dst1) // [1,2,3]
或者使用append方法
src := []int{1, 2, 3}
var dst1 []int
dst1 = append(dst1, src...)
fmt.Println(dst1) // [1,2,3]
append方法既可以加元素,也可以加切片,真是活见久。
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
8. range slice不是随机,range map是随机的
issue:
func main() {
ss := []int{11, 4, 5, 2, 7}
for i, item := range ss {
fmt.Printf(" %d : %d \r\n", i, item)
}
mm := map[string]int{"11": 11, "4": 4, "5": 5, "2": 2, "7": 7}
for i, m := range mm {
fmt.Printf(" %s : %d \r\n", i, m)
}
}
输出:
0 : 11
1 : 4
2 : 5
3 : 2
4 : 7 // slice
4 : 4 // map
5 : 5
2 : 2
7 : 7
11 : 11
9. 让人困惑的for-range 循环
golang中除了经典的三段式for循环外,还有帮助快速遍历 slice array map channel的 for range循环。
issue1:for range中操作迭代变量,原切片竟然没影响。
func main() {
ss := []int{1, 1, 1}
for _, x := range ss {
x = x + 1
}
fmt.Println(ss) // [1,1,1]
}
solution:操作索引值
func main() {
ss := []int{1, 1, 1}
for i,_ := range ss {
ss[i] += 1
}
fmt.Println(ss) // [2,2,2]
}
issue2: 这也是一个有意思的case, 迭代体内对于[修改array元素值]无意识, 对于[修改slice元素值]有意识, 活见久。
func main() {
aa := [2]int{0, 0}
for _, x := range aa {
fmt.Println(x) // print 0,0
aa[1] = 8
}
fmt.Println(aa) // print [0,8]
}
solution: 将array换成slice
ss := []int{0, 0}
for _, x := range ss {
fmt.Println(x) // print 0,8
ss[1] = 8
}
fmt.Println(ss) // print [0,8]
以上问题的关键是:
所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新变量 ha,在赋值的过程中就发生了拷贝,而我们又通过 len 关键字预先获取了切片的长度,所以在循环中追加新的元素也不会改变循环执行的次数,这也就解释了上面提到的现象。
而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,赋值时也会触发拷贝。
ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
v2 := nil
for ; hv1 < hn; hv1++ {
tmp := ha[hv1]
v1, v2 = hv1, tmp
...
}
C#中没有这么多诡异的情况。
C#数组是定长数组,一旦被创建,数组大小就无法改变;
span 带有底层数组指针和长度,但是长度也是只读,是类型安全、内存安全的滑块。
10. nil值比较
issue: golang中:一个接口等于另一个接口,前提是它们的类型和动态值相同。这同样适用于nil值。
func Foo() error {
var err *os.PathError = nil
return err
}
func main() {
err := Foo()
fmt.Println(err) // print: <nil>
fmt.Println(err == nil) // print: false
}
solution: 强转为同一类型
fmt.Println(err == (*os.PathError)(nil)) // print: true
或者显式返回nil error
func returnsError() error {
if bad() {
return ErrBad
}
return nil
}
在底层,接口被实现为两个元素,一个类型T和一个值V,V是一个具体的值,比如int、结构体或指针,而不是接口本身,它的类型是T, 上面的错误示例中: err 具备了T=*MyError, V=nil 的实现,故与nil不等。
只要记住,如果接口中存储了任何具体的值,该接口将不会为nil.
最后再提供几张图,供大家参考,也许上面的坑位能柳暗花明。
(1)

[4]int在内存中的表示形式只是按顺序排列的四个整数值:
(2)

s = make([]byte,5)
切片是数组片段的描述符,它由指向数组的指针、片段的长度和它的容量(片段的最大长度)组成。
当我们对s进一步切片: s =s[2,4]

C# span是指向一段连续内存的类型安全的、内存安全的视图,也有数组指针和长度length,不过他的length是只读定长的,也不会有扩容的动作。
(3)

m := make(map[string]string)
m是指向Map Header数据结构的指针,Map Header包含了关于map的所有元信息:
- map中当前的条目数
- map中的桶数总是等于2的幂,因此存储log(桶)以保持较小的值
- 指向连续内存位置的桶数组的指针
- 创建不同的map的哈希种子是随机
https://phati-sawant.medium.com/internals-of-map-in-golang-33db6e25b3f8
问道Golang,6月龄必知必会(二)的更多相关文章
- 闻道Go语言,6月龄必知必会
大家好,我是马甲哥, 学习新知识, 我的策略是模仿-->归纳--->举一反三, 在同程倒腾Go语言一年有余,本次记录<闻道Go语言,6月龄必知必会>,形式是同我的主力语言C#做 ...
- 读书笔记汇总 - SQL必知必会(第4版)
本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...
- 读书笔记--SQL必知必会--建立练习环境
书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL in 10 Minutes - Fourth Edition> MyS ...
- 读书笔记--SQL必知必会12--联结表
12.1 联结 联结(join),利用SQL的SELECT在数据查询的执行中联结表. 12.1.1 关系表 关系数据库中,关系表的设计是把信息分解成多个表,一类数据一个表,各表通过某些共同的值互相关联 ...
- 读书笔记--SQL必知必会18--视图
读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...
- 《MySQL 必知必会》读书总结
这是 <MySQL 必知必会> 的读书总结.也是自己整理的常用操作的参考手册. 使用 MySQL 连接到 MySQL shell>mysql -u root -p Enter pas ...
- 《SQL必知必会》学习笔记(一)
这两天看了<SQL必知必会>第四版这本书,并照着书上做了不少实验,也对以前的概念有得新的认识,也发现以前自己有得地方理解错了.我采用的数据库是SQL Server2012.数据库中有一张比 ...
- SQL 必知必会
本文介绍基本的 SQL 语句,包括查询.过滤.排序.分组.联结.视图.插入数据.创建操纵表等.入门系列,不足颇多,望诸君指点. 注意本文某些例子只能在特定的DBMS中实现(有的已标明,有的未标明),不 ...
- .NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上下文环境检查(实战)
Microsoft.NET 解决方案,项目开发必知必会. 从这篇文章开始我将分享一系列我认为在实际工作中很有必要的一些.NET项目开发的核心技术点,所以我称为必知必会.尽管这一系列是使用.NET/C# ...
- 0005 《SQL必知必会》笔记01-SELECT语句
1.SELECT基本语句: SELECT 字段名1,···,字段名n FROM 表名 2.检索所有字段,用"*"替换字段名,这会导致效率低下 SELECT * FROM 表名; 3 ...
随机推荐
- Ajax JavaScript传参-javascript加载本地文件
get请求需要拼接到url里面 post请求,需要放到send方法里面,后端取值的时候,Ajax用JavaScript传输跟用postman传输,取值方式有些区别,这里做一下详细记录 传参格式{'id ...
- 实时中文语音克隆——开源项目MockingBird体验
[引子] 在今年大型网络攻防演练前不久,笔者接到一个公司的座机号码来电,上来就问防守准备得怎么样了,哪里还有不足等.等等,这声音不认识,笔者第一反应就是蓝军(Red Team)来进行社会工程攻击,于是 ...
- php excel导出列超过26个字母处理
/** * String from columnindex * * @param int $pColumnIndex Column index (base 0 !!!) * @return strin ...
- Jmeter的简单配置和使用
对于测试大佬来说,最常见的压测工具莫过于Apache旗下的 JMeter . 那么测试大佬经常使用,对于我们后端开发者来说,也是属于必备的一个技能. 接下来咱们就一起简单的配置一下烫手的jmeter ...
- MySQL之char、varchar、text类型
在存储字符串时, 可以使用char.varchar或者text类型, 那么具体使用场景呢? 参考下面这个表结构: 分析 一,char类型 char列的长度固定为创建表时声明的长度.长度可以为从0到25 ...
- 物联网之Wifi协议
今天来重点介绍一下WIfi协议,咱们用的其实已经很多了. 主要内容: ⼀.基本概述 ⼆.实践基础 三.⼀些原理 ⼀.基本概述 ============================ 1.有线和⽆线⽹ ...
- 各种相机以及图片-SLAM14CP5
--2020.10.20 开始学习SLAM.想着从SLAM开始然后做三维重建.前面的李群李代数以及旋转四元数有点复杂.都看过了一遍.但不太理解就先放放.希望接下去能够顺利进行.数学基础可能不是很好,公 ...
- 作业一:PCA降维练习
作业一:PCA降维作业 代码 点击查看代码 #author:qiao_px #@Time 2022/10/31 16:11 #@File ceshiPCA.py import pandas as pd ...
- idea 部署项目到 docker 运行
1.在远程服务器上开启 docker 远程连接 $vim /usr/lib/systemd/system/docker.service # ExecStart=/usr/bin/dockerd -H ...
- python使用openpyxl读取合并单元格的值(转)
目录问题:解决思路:问题:假设在test.xlsx的"Sheet1"工作表中,A1:D3区域的值如下:要求给定指定的行.列以及对应的工作表作为参数,能够正确解析合并单元格,获取指定 ...