资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

go语言字符串数据结构 go 字符串函数

Go小知识新解

1、值接收者和指针接收者

成都创新互联专业为企业提供正定网站建设、正定做网站、正定网站设计、正定网站制作等企业网站建设、网页设计与制作、正定企业网站模板建站服务,10多年正定做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

所谓指针接收者和值接收者这两个概念,用GO写了一阵子代码的人都了解了,这里只做简要说明一下,也就是对于一个给定结构,咱们对结构进行方法包装的时候,固定必传的参数,用来指向这个对象结构自身的一个参数,在go中也就是形式如下:

我们对结构体testStruct进行了包装,提供了两个方法,sum和modify,其中sum的方法接收者为a testStruct,这个就是值接收者,而modify的接收者为a *testStruct就是指针接收者,也就是说固定对象指针,一个传递的是指针地址,而另外一个直接传递的是结构值拷贝了

对指针有一定了解的,都可以知道,指针传递过去的,可以直接修改结构内部内容,而值传递过去的,无论如何修改这个接收者的数据,不会对原对象结构产生影响。而对于咱们包装结构对象的时候,到底是使用指针还是使用值接收者,这个实际上没有太大的定论,就我个人的观点来说,如果结构体占有的内存空间不大(kb级别),而又不需要修改内部的,同时结构对象内部没有同步对象比如(sync包中的mutex,rwlock,waitgroup等之类的结构的话,可以直接值传递,实际上值copy也没有咱们想象的那么慢,很多时候,都用指针,最后的gc回收扫描可能都比咱们这个传递copy的消耗大) p="" /kb级别),而又不需要修改内部的,同时结构对象内部没有同步对象比如(sync包中的mutex,rwlock,waitgroup等之类的结构的话,可以直接值传递,实际上值copy也没有咱们想象的那么慢,很多时候,都用指针,最后的gc回收扫描可能都比咱们这个传递copy的消耗大)

2、实现接口的值接收者和指针接收者有啥区别

也就是比如定义如下

这里面的值接收者和指针接收者有什么区别,这里咱来写一个测试

通过这个测试用例可以发现,指针接收者实现的接口可以同时支持转移到值接收者接口和指针接收者接口,而用值接收者实现的接口,则无法转移到使用指针接收者实现的接口,为啥子呢?目前网上或者各类资料上都是给的一个很官方很官方,而且很书面话难以理解的说明,大致意思如下:

这是目前网络或者各种资料上都是差不多是这样说的,看似讲了,实际上就说了一个结果,根本就没说出来一个为什么。这样的总结出来,一个初学者的角度来看,是很不好理解的,初学者要么就是死记硬背,要么就是生搬硬套,甚至直到写了好多好多代码了,都还没有搞明白一个为啥子,只是会用了而已,从长远来说这是不利于自身提高的。

有这两个本质点,咱们自己来思考一下,如果你来实现这个编译器的时候,用指针接收的时候,指针接收者,默认就能直接获取支持,而值接收者实现接口的咱们可以直接来一个解指针就变成了值,就能匹配上值接收者实现的接口了,反过来说,如果值接收者,此时要匹配指针接收者,如何匹配呢,取一个地址就变成了指针了,此时数据类型确实是匹配了,但是,地址指向的数据区不对了,因为我们刚刚说了值接收者拷贝了一个新值之后是完全的一个新的对象,这个新对象和原始对象一点关系都没有,咱们取地址,取的也是这个新对象地址,对这个地址进行操作,也是这个新对象的内部数据,和原始数据内部没有任何关系,所以由此就能推断出,这个是为啥子值接收者不能匹配上指针接收者,而指针接收者却可以匹配上值接收者了。

1、在某个作用域内部,所有定义的字符串的数据区相同

这个很好验证,代码如下:

2、字符串相加会产生一个新串

这个也很好验证

3、字符串真的是不可变的吗

实际上从字符串的结构

从这个结构,就能大致的推断出来,字符串设计成这样就不具备直接扩容+来增加新数据,而如果咱们直接使用string[index] = 'a',用这种方式,就不能编译通过,官方也确定说字符串是不可变的。那么真的是不可变的吗?

通过上面的结构,在加上go的slice切片的数据结构

由此可见,咱们可以将字符串通过指针方式强转为一个byte数组指针,然后通过byte切片来修改,试试

编译通过,运行报错

unexpected fault address 0xae2e27

fatal error: fault

这个错误,基本上就是一个内存的保护错误,是写异常,所以说明了,这个肯定做了内存写保护,那么直接修改一下内存区的属性,去掉他的写保护,就能写了

以下代码都是在Win平台,Go1.18,Win上修改内存权限属性,使用VirtualProtect,代码如下

此时运行,就能发现tstr的内容被咱们变了,这种情况实际上在实际开发中不具有实际意义,因为本身在语言层面,已经做了层层限制,咱们这是属于非法强制的操作方式,是流氓行为,那么是否有比较温和一点的操作方式呢?答案是有的,且往下看。

通过上面,我们已经用到了字符串结构,切片结构,要想字符串内容可变,那么咱们自己构造字符串的数据内容区域,且让这个数据区木有内存写保护不就行了,内容区可变,GO原生态的byte数组不就行嘛,所以咱们自己构造一下

此时我们直接修改buffer的内容,就是直接修改了str的数据内容了。而又不会像前面的一样遇到内存写保护

4、字符串转换优化时可能碰到的坑

通过前面讨论的字符串的可变性的方法,咱们可以知道,很多时候,[]byte到字符串的转变,可以直接构造其结构,而共享数据,从而达到减少数据内存copy的方式来进行优化,再使用这些优化的时候,一定需要注意,字符串或者数组的生命周期,是否会存在被改写的情况,从而导致前后不一致的问题。

比如下面这段代码:

大家可以猜想一下,这个最后里面的数据mmp中,"test"的value是多少,"abcd"的value是多少,然后想想为什么,且等端午之后,再来分解

golang获取到string和直接赋值strimg不一样

1、 string的定义

Golang中的string的定义在reflect包下的value.go中,定义如下:

StringHeader 是字符串的运行时表示,其中包含了两个字段,分别是指向数据数组的指针和数组的长度。

// StringHeader is the runtime representation of a string.

// It cannot be used safely or portably and its representation may

// change in a later release.

// Moreover, the Data field is not sufficient to guarantee the data

// it references will not be garbage collected, so programs must keep

// a separate, correctly typed pointer to the underlying data.

type StringHeader struct {

Data uintptr

Len int

}

1

2

3

4

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10

2、string不可变

Golang中的字符串是不可变的,不能通过索引下标的方式修改字符串中的数据:

在这里插入图片描述

运行代码,可以看到编译器报错,string是不可变的

在这里插入图片描述

但是能不能进行一些骚操作来改变元素的值呢?

package main

import (

"fmt"

"reflect"

"unsafe"

)

func main() {

a := "hello,world"

b := a[6:]

bptr := (*reflect.StringHeader) (unsafe.Pointer(b))

fmt.Println(a)

fmt.Println(b)

*(*byte)(unsafe.Pointer(bptr.Data)) = '.'

fmt.Println(a)

fmt.Println(b)

}

// 运行结果

hello,world

world

unexpected fault address 0x49d7e3

fatal error: fault

[signal 0xc0000005 code=0x1 addr=0x49d7e3 pc=0x4779fa]

goroutine 1 [running]:

runtime.throw(0x49c948, 0x5)

C:/Program Files/Go/src/runtime/panic.go:1117 +0x79 fp=0xc0000dbe90 sp=0xc0000dbe60 pc=0x405fd9

runtime.sigpanic()

C:/Program Files/Go/src/runtime/signal_windows.go:245 +0x2d6 fp=0xc0000dbee8 sp=0xc0000dbe90 pc=0x4189f6

main.main()

F:/go_workspace/src/code/string_test/main.go:20 +0x13a fp=0xc0000dbf88 sp=0xc0000dbee8 pc=0x4779fa

runtime.main()

C:/Program Files/Go/src/runtime/proc.go:225 +0x256 fp=0xc0000dbfe0 sp=0xc0000dbf88 pc=0x4087f6

runtime.goexit()

C:/Program Files/Go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc0000dbfe8 sp=0xc0000dbfe0 pc=0x435da1

Process finished with the exit code 2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

在上面的代码中,因为在go语言中不能进行指针的加减运算,因此取切片,让b的Data指针指向’,'所在的位置。然后把"hello,world"中的逗号改为点,但是发现还是不行,程序直接崩溃了。看来go语言中的指针得到了大大的限制,设计者并不想让程序员过度使用指针来写出一些不安全的代码。

3、使用string给另一个string赋值

Golang中的字符串的赋值并不是拷贝底层的字符串数组,而是数组指针和长度字段的拷贝。例如:当我们定义了一个字符串 a := “hello,world” 然后定义了 b := a 底层所做的操作只是创建了两个StringHeader的结构体,它们的Data字段都指向同一段数据,如下图:

在这里插入图片描述

我们可以利用代码来证实这一点:

package main

import (

"fmt"

"reflect"

"unsafe"

)

func main() {

a := "hello,world"

b := a

fmt.Println(a)

fmt.Println(b)

aptr := (*reflect.StringHeader) (unsafe.Pointer(a))

bptr := (*reflect.StringHeader) (unsafe.Pointer(b))

fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))

fmt.Println("b ptr:", unsafe.Pointer(bptr.Data))

}

// 运行结果

hello, world

hello, world

a ptr: 0x6bdb76

b ptr: 0x6bdb76

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

在上面的代码中,将a和b转换为StringHeader类型的指针,然后分别打印出,a和b的Data指针的值,发现是相同的

那么如果对a做切片赋值给b呢?

func main() {

a := "hello,world"

b := a[6:]

fmt.Println(a)

fmt.Println(b)

aptr := (*reflect.StringHeader) (unsafe.Pointer(a))

bptr := (*reflect.StringHeader) (unsafe.Pointer(b))

fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))

fmt.Println("b ptr:", unsafe.Pointer(bptr.Data))

}

// 运行结果

hello,world

world

a ptr: 0xd4d849

b ptr: 0xd4d84f

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

0xd4d849 - 0xd4d84f = 0x000006

显然,也没有分配新的数组并拷贝数据,而是将原字符数组的指针的偏移赋给了b的StringHeader的Data

4、string重新赋值

如果对一个已经赋值的字符串重新赋值,也不会修改原内存空间,而是申请了新的内存空间,对其赋值,并指向新的内存空间。如下图:

在这里插入图片描述

也可以使用代码来证实一下:

package main

import (

"fmt"

"reflect"

"unsafe"

)

func main() {

a := "hello,world"

aptr := (*reflect.StringHeader) (unsafe.Pointer(a))

fmt.Println("a ptr:", unsafe.Pointer(aptr.Data))

fmt.Println("a len", aptr.Len)

a = "hello,golang"

newAPtr := (*reflect.StringHeader) (unsafe.Pointer(a))

fmt.Println("b ptr:", unsafe.Pointer(newAPtr.Data))

fmt.Println("b len:", newAPtr.Len)

}

// 运行结果

a ptr: 0x3ed7f4

a len 11

b ptr: 0x3edb2c

b len: 12

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

文章知识点与官方知识档案匹配

Go技能树九阴真经字符串

2043 人正在系统学习中

点击阅读全文

打开CSDN APP,看更多技术内容

Golang底层原理剖析之string类型与字符编码_cheems~的博客_g...

string类型 string结构 go语言中默认使用的是UTF-8编码 string由两部分组成,一部分是指向字符串起始地址的指针,另一部分是字节个数len注意不是字符个数,是字节个数!这个数据类型占用16B空间,指向字符串起始地址的指针和存在字节个数的整...

继续访问

...底层系列二(基础)_GoGo在努力的博客_golang string底层

1、 string的定义 Golang中的string的定义在reflect包下的value.go中,定义如下: StringHeader 是字符串的运行时表示,其中包含了两个字段,分别是指向数据数组的指针和数组的长度。 // StringHeader is the runtime representation of a...

继续访问

最新发布 Golang: []string 和 ...string

… 参数语法形成了可变参数的参数。它将接受零个或多个string参数,并将它们作为切片引用

继续访问

常用的Golang字符串(string)操作

文章目录Golang中常用的字符串操作一、标准库相关的Package二、常用字符串操作判断是否为空字符串 正文 Golang中常用的字符串操作 一、标准库相关的Package 二、常用字符串操作 判断是否为空字符串 思路:直接判断是否等于""空字符串,由于golang中字符串不能为nil,且为值类型,所以直接与空字符串比较即可 举例: str := "" if str == ""{...

继续访问

golang中的string_xiaodongdonga的博客_golang ...string

golang中的string 在golang中,string其实是个结构体,如图 ,在这个结构体中有两个值,一个是指向字符数组的指针str,另一个是这个字符串的长度len。 另外在golang中用的是UFT8边长编码,里面字母是用一个字节,西方的文字使用两个字节,...

继续访问

Golang中对字符串string的相关操作_Codex_97的博客_go 字符...

golang中拥有string的内置类型,可以使用与普通slice类型相似的性质。 1.简介 strings包提供了很多操作字符串的简单函数,通常一般的字符串操作都可以在这个包中找到。 strconv 包提供了基本数据类型和字符串之间的转换。在Go 中,没有隐式...

继续访问

go-string-set:一种在GoLang中创建字符串集的方法

概述 通过字符串设置方法。 用法 go get github.com/wojnosystems/go-string-set package main import ( "github.com/wojnosystems/go-string-set/string_set" "strings" ) func main () { myPeople := string_set . New () myPeople . Add ( "Bob" ) myPeople . Add ( "Jane" ) myPeople . Add ( "Gary" ) myPeople . Add ( "Bob" ) if myPeople . Exists ( "Bob" ) { // Bob exists in myPeople, so this code block execute

Golang:strings包和字符串操作

strings包 string类型值是不可变的,如果想要获得一个不一样的字符串,就只能基于原字符串进行剪裁,拼接等操作,然后将得到的新字符串存放到一块联系内存中。 string值包含了指向底层字节数组头部的指针值,以及该字节数组的长度。在string值上做切片,就相当于在其底层字节数组做切片。 而字符串拼接时(使用+),会把所有被拼接的字符串依次拷贝到一个崭新且足够大的连续内存空间中,并把持有新...

继续访问

golang中的strings.SplitN_盼盼编程的博客_strings.splitn

golang中的字符串操作strings.SplitN package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "hello world hello world" //str := "wo" //以str为分隔符,将s切分成多个子串,结果中**不包含*...

继续访问

Golang_String常用操作_itzhuzhu.的博客_golang strings

func main() { var x string = "ITzhuzhu" var y string = "itzhuzhu" fmt.Println(strings.EqualFold(x, y)) //true fmt.Println(x == y) //false } 1 2 3 4 5 6 返回子串在字符串第一次出现的索引,如果没有则...

继续访问

Golang类型的String()方法

作用 用于定制fmt.Println(x)、fmt.Printf("%v", x)、fmt.Print(x)时输出的内容。 示例 package main import "fmt" type Couple struct { Husband string Wife string } func (self Couple) String() string { return "(husband:" + self.Husband + ", wife:" + self.Wife +

继续访问

golang的string

golang中的string是不可变的字节序列,零值是空字符串,默认是UTF-8编码。 golang中使用字符串最需要注意的是,golang中的字符串是字节序列,string == []byte,由于一个字符占用的字节数不确定,所以无法通过下标[i]的方式稳定地获取对应位置的字符。 也就意味着字符串的第i个字节,不一定就是第i个字符,原因请参考 unicode与UTF-8 文字符号在golang中被称为rune,发音为/ru:n/,意思是符号。由于utf-8最多使用4个字节来编码,所以rune类型是int3

继续访问

golang中的strings.Trim_盼盼编程的博客_strings.trim

golang中的字符串操作strings.Trim package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "Hello world hello world" str := "world" //var s = []string{"11","22","33"} //删除s首尾...

继续访问

Golang 字符串

Go语言的代码是由Unicode字符组成的,它们都必须由Unicode编码规范中的UTF-8编码格式进行编码并存储,Unicode编码规范中的编码格式定义的是字符与字节序列之间的转换方式。其中的UTF-8是一种可变宽的编码方案,它会用一个或多个字节的二进制数来表示某个字符,最多使用四个字节。Go语言中的一个string类型值会由若干个Unicode字符组成,每个Unicode字符都可以由一个rune类型的值来承载。stringforrange。...

继续访问

golang字符串[]string(slice)去重

1、现实代码 // []string 去重 func RemoveDuplicate(list []string) []string { // 这个排序很关键 sort.Strings(list) i := 0 var newlist = []string{""} for j := 0; j len(list); j++ { if strings.Compare(newlist[i], list[j]) == -1 { newlist = append(newlist, lis

继续访问

深入理解golang string

golang string string的定义 // string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string string里存储的是字符

继续访问

golang中string包

对于基本类型来说,字符串所需要执行的操作会比较复杂,所以一般语言都会额外封装一些方法用于处理字符串,go语言标准库中也存在这样一个名为strings的库 包含判断,判断一个字符中是否有相应的某个子字符串是经常遇到的一种字符串操作,再go语言中可以使用strings包中的两个方法判断 ...

继续访问

golang中的strings.TrimLeft

golang中的字符串操作strings.TrimLeft package main import ( "fmt" "strings" ) //golang字符串操作 func main(){ s := "Hello world hello world" str := "Hello" //var s = []string{"11","22","33"} //删除s头部连续的包含在str中的字符串 .

继续访问

golang入门time与string转换, time加减时间, 两个时间差

package mainimport ( "fmt" "time")var timeLayoutStr = "2006-01-02 15:04:05" //go中的时间格式化必须是这个时间//var timeLayoutStr = 2006/01/02 03:04:05 //合法, 格式可以改变//var timeLayoutStr = 2019/01/02 15:04:05 /...

继续访问

Golang——string

1.string结构体底层 由两部分组成:指向底层[ ]byte数组的指针;长度(类似切片结构体) 2.相同的string常量不会重复存储 //由s1、s2的指针指向一块相同的只读内存,这段内存以utf-8编码存放hello数组 s1 := "hello" s2 := "hello" 3.string常量会在编译期分配到只读段,对应数据地址不可写入,故string不支持修改。 要修改必须转[]byte,string和[]byte转换,会将这段只读内存的数据复制到堆/栈上。 //wrong s := "he

继续访问

热门推荐 golang -----------字符串(rune,string,type)

一、内存布局 字符串在Go语言内存模型中用一个2字长的数据结构表示。它包含一个指向字符串存储数据的指针和一个长度数据。因为string类型是不可变的,对于多字符串共享同一个存储数据是安全的。切分操作str[i:j]会得到一个新的2字长结构,一个可能不同的但仍指向同一个字节序列(即上文说的存储数据)的指针和长度数据。这意味着字符串切分可以在不涉及内存分配或复制操作。这使得字符串切分的效率等同于...

继续访问

Golang string 常用方法

strings.Compare(p1, p2) 判断p1是否大于p2,大于:1,小于:-1,等于:0。2.strings.LastIndex(p1,p2) p2在p1中最后一次出现的位置,未出现返回-1。1.strings.Replace(p1,p2,p3,n) 将p1中的p2替换成p3,最多替换n个。1.strings.Index(p1,p2) p2在p1中第一次出现的位置,未出现返回-1。2.strings.ReplaceAll(p1,p2,p3) 将p1中的所有p2替换成p3。

继续访问

golang中的字符串

在go中rune是一个unicode编码点。 我们都知道UTF-8将字符编码为1-4个字节,比如我们常用的汉字,UTF-8编码为3个字节。所以rune也是int32的别名。

继续访问

golang之字符串

Go语言的字符有两种类型: 一种是byte型,或者叫uint8类型,代表了ASCII码的一个字符。 一种是rune类型,或者叫int32类型,代表一个UTF-8字符,当需要处理中文、日文等unicode字符时,则需要用到rune类型。 ...

继续访问

Golang的 string 类型

一点睛 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 二 string 使用注意事项和细节 1 Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本,这样 Golang 统一使用 UTF-8 编码,中文乱码问题不会再困扰程序员。 2 字符串一旦赋值了,字符串就不能修改了,在 Go 中字符串是不可变的。 3 字符串的两种表示形式。 a 双引号——会识别转义字符 .

继续访问

Golang Strings 官方包常用方法,学会这些够用了

1.判断是否以某字符串打头/结尾 2.字符串分割 3.返回子串索引 4.字符串连接 5.字符串转化为大小写 6.统计某个字符在字符串出现的次数 7.判断字符串的包含关系 8.字符串替换

这样的数据结构要怎么设计go语言的struct?

一般送得及时的我都会给好评,迟到点的说一句不好意思什么的,我也会说一声没事,然后再给好评,只要不是服务态度问题,能说服我接受问题的理由的都能给好评。不过这个看人吧,遇到暴躁点的客户无论说什么也不接受的我也没法子。

Go数据结构篇

1、基本数据类型

bool

string

int int8 int16 int32 int64

uint uint8 uint16 uint32 uint64 uintptr

byte // alias for int8

rune // alias for int32,represents a Unicode code point

float32 float64

complex64 complex128

常量定义

2、类型转换

(1)Go语言不允许隐式类型转换(不支持小位数类型向大位数类型转)

(2)别名和原有类型也不能进行隐式类型转换(type MyInt int64 = int64)

3、类型的预定义值

1.math.MaxInt64

2.math.MaxFloat64

3.math.MaxUInt32

4、指针类型

(1)不支持指针运算

(2)string是值类型,其默认的初始化值为空字符串,而不是nil

5、算术运算符

+ - * / % ++ --(不支持前置++ --)

6、比较运算符

#== != = =

(1)比较数组

相同维数且含有形同个数元素的数组才可以比较

每个元素都相同的才相等

7、位运算符

| ^

^ (按位置零) a (^b)

1 ^ 0 1

1 ^ 1 0

0 ^ 1 0

0 ^ 0 0

8、条件与循环

(1)循环

Go 语⾔仅⽀持循环关键字 for

(2)条件

9、数组和切片

数组截取,索引下标从0开始计数

a[开始索引(包含), 结束索引(不包含)]

a := [...]int{1, 2, 3, 4, 5}

a[1:2] //2

a[1:3] //2,3

a[1:len(a)] //2,3,4,5

a[1:] //2,3,4,5

a[:3] //1,2,3

切片内部结构

9、Map

9、字符串

Unicode UTF8

常⽤字符串函数

没有类,C语言有结构体,那么Go的结构体有什么特别之处?

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。

自定义类型

在Go语言中有一些基本的数据类型,如string、整型、浮点型、布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型。

自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:

通过Type关键字的定义,MyInt就是一种新的类型,它具有int的特性。

类型别名

类型别名是Go1.9版本添加的新功能。

类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。

type TypeAlias = Type

我们之前见过的rune和byte就是类型别名,他们的定义如下:

类型定义和类型别名的区别

类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。

结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是int。MyInt类型只会在代码中存在,编译完成时并不会有MyInt类型。

Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。

Go语言中通过struct来实现面向对象。

结构体的定义

使用type和struct关键字来定义结构体,具体代码格式如下:

其中:

举个例子,我们定义一个Person(人)结构体,代码如下:

同样类型的字段也可以写在一行,

这样我们就拥有了一个person的自定义类型,它有name、city、age三个字段,分别表示姓名、城市和年龄。这样我们使用这个person结构体就能够很方便的在程序中表示和存储人信息了。

语言内置的基础数据类型是用来描述一个值的,而结构体是用来描述一组值的。比如一个人有名字、年龄和居住城市等,本质上是一种聚合型的数据类型

结构体实例化

只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。

基本实例化

举个例子:

我们通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。

匿名结构体

在定义一些临时数据结构等场景下还可以使用匿名结构体。

创建指针类型结构体

我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:

从打印的结果中我们可以看出p2是一个结构体指针。

需要注意的是在Go语言中支持对结构体指针直接使用.来访问结构体的成员。

取结构体的地址实例化

使用对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。

p3.name = "七米"其实在底层是(*p3).name = "七米",这是Go语言帮我们实现的语法糖。

结构体初始化

没有初始化的结构体,其成员变量都是对应其类型的零值。

使用键值对初始化

使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。

也可以对结构体指针进行键值对初始化,例如:

当某些字段没有初始值的时候,该字段可以不写。此时,没有指定初始值的字段的值就是该字段类型的零值。

使用值的列表初始化

初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:

使用这种格式初始化时,需要注意:

结构体内存布局

结构体占用一块连续的内存。

输出:

【进阶知识点】关于Go语言中的内存对齐推荐阅读:在 Go 中恰到好处的内存对齐

面试题

请问下面代码的执行结果是什么?

构造函数

Go语言的结构体没有构造函数,我们可以自己实现。 例如,下方的代码就实现了一个person的构造函数。 因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。

调用构造函数

方法和接收者

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。

方法的定义格式如下:

其中,

举个例子:

方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。

指针类型的接收者

指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为Person添加一个SetAge方法,来修改实例变量的年龄。

调用该方法:

值类型的接收者

当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。

什么时候应该使用指针类型接收者

任意类型添加方法

在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。

注意事项: 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。

结构体的匿名字段

匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。

嵌套结构体

一个结构体中可以嵌套包含另一个结构体或结构体指针。

嵌套匿名结构体

当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。

嵌套结构体的字段名冲突

嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段。

结构体的“继承”

Go语言中使用结构体也可以实现其他编程语言中面向对象的继承。

结构体字段的可见性

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

结构体与JSON序列化

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号:分隔,然后紧接着值;多个键值之间使用英文,分隔。

结构体标签(Tag)

Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

`key1:"value1" key2:"value2"`

结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔。 注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。

例如我们为Student结构体的每个字段定义json序列化时使用的Tag:

go怎么数据格式能放数组

Go语言提供了数组类型的数据结构。根据查询相关公开信息显示,数组时具有相同唯一类型的一组已编号且长度固定的数据项序列,中类型可以是任意的原始类型如整形、字符串或者自定义类型。


文章标题:go语言字符串数据结构 go 字符串函数
本文链接:http://cdkjz.cn/article/doooood.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

业务热线:400-028-6601 / 大客户专线   成都:13518219792   座机:028-86922220