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的主要内容,如果未能解决你的问题,请参考以下文章

Golang basic_leaming函数详解

Golang basic_leaming函数详解

Golang basic_leaming4 函数

Golang basic_leaming4 函数

Golang basic_leaming基本数据类型

Golang basic_leaming基本数据类型