Golang basic_leaming常用函数 C
Posted 知其黑、受其白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang basic_leaming常用函数 C相关的知识,希望对你有一定的参考价值。
阅读目录
基础
如何高效地拼接字符串
拼接字符串的方式有:
+
- fmt.Sprintf
- strings.Builder
- bytes.Buffer
- strings.Join
1 “+”
使用+操作符进行拼接时,会对字符串进行遍历,计算并开辟一个新的空间来存储原来的两个字符串。
2 fmt.Sprintf
由于采用了接口参数,必须要用反射获取值,因此有性能损耗。
3 strings.Builder
用 WriteString() 进行拼接,内部实现是指针+切片,同时String()返回拼接后的字符串,它是直接把 []byte 转换为string,从而避免变量拷贝。
4 bytes.Buffer
bytes.Buffer是一个一个缓冲byte类型的缓冲器,这个缓冲器里存放着都是byte,
bytes.buffer底层也是一个[]byte切片。
5 strings.join
strings.join 也是基于strings.builder来实现的,并且可以自定义分隔符,在join方法内调用了b.Grow(n)方法,这个是进行初步的容量分配,而前面计算的n的长度就是我们要拼接的slice的长度,因为我们传入切片长度固定,所以提前进行容量分配可以减少内存分配,很高效。
性能比较:
strings.Join ≈ strings.Builder > bytes.Buffer > "+" > fmt.Sprintf
5种拼接方法的实例代码
package main
import (
"bytes"
"fmt"
"strings"
)
func main()
a := []string"a", "b", "c"
//方式1:+
ret := a[0] + a[1] + a[2]
//方式2:fmt.Sprintf
ret := fmt.Sprintf("%s%s%s", a[0], a[1], a[2])
//方式3:strings.Builder
var sb strings.Builder
sb.WriteString(a[0])
sb.WriteString(a[1])
sb.WriteString(a[2])
ret := sb.String()
//方式4:bytes.Buffer
buf := new(bytes.Buffer)
buf.Write(a[0])
buf.Write(a[1])
buf.Write(a[2])
ret := buf.String()
//方式5:strings.Join
ret := strings.Join(a, "")
什么是 rune 类型
ASCII 码只需要 7 bit 就可以完整地表示,但只能表示英文字母在内的128个字符,为了表示世界上大部分的文字系统,发明了 Unicode, 它是ASCII的超集,包含世界上书写系统中存在的所有字符,并为每个代码分配一个标准编号(称为Unicode CodePoint),在 Go 语言中称之为 rune,是 int32 类型的别名。
Go 语言中,字符串的底层表示是 byte (8 bit) 序列,而非 rune (32 bit) 序列。
sample := "我爱GO"
runeSamp := []rune(sample)
runeSamp[0] = '你'
fmt.Println(string(runeSamp)) // "你爱GO"
fmt.Println(len(runeSamp)) // 4
一个汉字 = 2字节
1字节(Byte)=8字位=8个二进制数
1字位(bit)=1个二进制数
1B=8b
1KB=1024B
1MB=1024KB
1GB=1024MB
b称为字位、B称为字节、KB称为千字节、MB称为兆字节、GB称为吉字节。
支持默认参数或可选参数吗?
不支持。但是可以利用结构体参数,或者…传入参数切片数组。
// 这个函数可以传入任意数量的整型参数
func sum(nums ...int)
total := 0
for _, num := range nums
total += num
fmt.Println(total)
如何交换 2 个变量的值?
- 变量
a,b = b,a;
- 指针
*a,*b = *b, *a
Go 语言 tag 的用处?
tag 可以为结构体成员提供属性。
常见的:
- json:序列化或反序列化时字段的名称
- db: sqlx 模块中对应的数据库字段名
- form: gin框架中对应的前端的数据字段名
- binding: 搭配 form 使用, 默认如果没查找到结构体中的某个字段则不报错值为空, binding为 required 代表没找到返回错误给前端。
如何获取一个结构体的所有tag?
利用反射:
package main
import (
"fmt"
"reflect"
)
type Author struct
Name int `json:Name`
Publications []string `json:Publication,omitempty`
func main()
t := reflect.TypeOf(Author)
for i := 0; i < t.NumField(); i++
name := t.Field(i).Name
s, _ := t.FieldByName(name)
fmt.Println(name, s.Tag)
PS E:\\TEXT\\test_go\\test\\case> go run .\\case.go
Name json:Name
Publications json:Publication,omitempty
PS E:\\TEXT\\test_go\\test\\case>
上述例子中,reflect.TypeOf方法获取对象的类型,之后NumField()获取结构体成员的数量。
通过 Field(i)
获取第 i
个成员的名字。
再通过其 Tag 方法获得标签。
如何判断 2 个字符串切片(slice) 是相等的?
这个是是顺序一致的比较,如果需要不一致的比较需要转成 map 类型。
reflect.DeepEqual()
, 但反射非常影响性能。
结构体打印时,%v 和 %+v 的区别
%v
输出结构体各成员的值;
%+v
输出结构体各成员的名称和值;
%#v
输出结构体名称和结构体各成员的名称和值;
Go 语言中如何表示枚举值(enums)?
在常量中用 iota 可以表示枚举。
iota从0开始。
const (
B = 1 << (10 * iota)
KiB
MiB
GiB
TiB
PiB
EiB
)
空 struct 的用途
用 map 模拟一个 set,那么就要把值置为 struct,struct 本身不占任何空间,可以避免任何多余的内存分配。
package main
import "fmt"
type Set map[string]struct
func main()
set := make(Set)
for _, item := range []string"A", "A", "B", "C"
set[item] = struct
fmt.Println(len(set)) // 3
if _, ok := set["A"]; ok
fmt.Println("A exists") // A exists
有时候给通道发送一个空结构体, channel<-struct
,也是节省了空间。
package main
func main()
ch := make(chan struct, 1)
go func()
<-ch
// do something
()
ch <- struct
// ...
仅有方法的结构体:type Lamp struct
go 里面的 int 和 int32 是同一个概念吗?
不是一个概念!千万不能混淆。
go语言中的 int 的大小是和操作系统位数相关的,如果是32位操作系统,int类型的大小就是4字节。如果是64位操作系统,int类型的大小就是8个字节。
除此之外uint也与操作系统有关。
int8占1个字节,int16占2个字节,int32占4个字节,int64占8个字节。
实现原理
init() 函数是什么时候执行的?
简答: 在 main 函数之前执行。
详细:
init() 函数是go初始化的一部分,由runtime初始化每个导入的包,初始化不是按照从上到下的导入顺序,而是按照解析的依赖关系,没有依赖的包最先初始化。
每个包首先初始化包作用域的常量和变量(常量优先于变量),然后执行包的init()函数。
同一个包,甚至是同一个源文件可以有多个init()函数。
init()函数没有入参和返回值,不能被其他函数调用,同一个包内多个init()函数的执行顺序不作保证。
执行顺序:import –> const –> var –>init()–>main()
一个文件可以有多个init()函数!
如何知道一个对象是分配在栈上还是堆上?
Go和C++不同,Go局部变量会进行逃逸分析。
如果变量离开作用域后没有被引用,则优先分配到栈上,否则分配到堆上。
那么如何判断是否发生了逃逸呢?
go build -gcflags '-m -m -l' xxx.go
关于逃逸的可能情况:
变量大小不确定,变量类型不确定,变量分配的内存超过用户栈最大值,暴露给了外部指针。
2 个 interface 可以比较吗 ?
Go 语言中,interface 的内部实现包含了 2 个字段,类型 T 和值 V,interface 可以使用 ==
或 !=
比较。
2 个 interface 相等有以下 2 种情况:
1、两个 interface 均等于 nil(此时 V 和 T 都处于 unset 状态)
2、类型 T 相同,且对应的值 V 相等。
看下面的例子:
package main
import "fmt"
type Stu struct
Name string
type StuInt interface
func main()
var stu1, stu2 StuInt = &Stu"Tom", &Stu"Tom"
var stu3, stu4 StuInt = Stu"Tom", Stu"Tom"
fmt.Println(stu1 == stu2) // false
fmt.Println(stu3 == stu4) // true
stu1 和 stu2 对应的类型是 *Stu
,值是 Stu 结构体的地址,两个地址不同,因此结果为 false。
stu3 和 stu4 对应的类型是 Stu,值是 Stu 结构体,且各字段相等,因此结果为 true。
2 个 nil 可能不相等吗?
可能不等。interface 在运行时绑定值,只有值为 nil 接口值才为 nil,但是与指针的 nil 不相等。
举个例子:
package main
import "fmt"
func main()
var p *int = nil
var i interface = nil
if p == i
fmt.Println("Equal")
两者并不相同。
总结:两个 nil 只有在类型相同时才相等。
以上是关于Golang basic_leaming常用函数 C的主要内容,如果未能解决你的问题,请参考以下文章