指针和结构体
1、指针
关于指针:通过前面的教程我们知道变量是用来存储数据的,变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量 a := 10,这个时候可以直接通过 a 这个变量来读取内存中保存的10这个值。在计算机底层a这个变量其实对应了一个内存地址。指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址。

不过我们写的代码中的所有地址,包括指针等,全都是虚拟地址,有兴趣可以去看看Linux栏目下的博客,具体讲述了进程虚拟内存、页表、物理内存之间的关系。
1.1、指针地址和指针类型
**每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、float、bool、string、array、struct) 都有对应的指针类型,如: *int、 int64、 string 等。

package main
import "fmt"
func main() {
var a = 10
var p = &a
fmt.Printf("a的值: %v, a的类型: %T, a的地址: %p\n", a, a, &a)
fmt.Printf("p的值: %v, p的类型: %T, p的地址: %p\n", p, p, &p)
}

1.2、指针取值
在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下:
a := 10
p := &a
fmt.Println(a, *p)
*p = 20
fmt.Println(a, *p)

总结:取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
例如实现一个交换函数:
package main
import "fmt"
func swap(x, y *int) {
tmp := *x
*x = *y
*y = tmp
}
func main() {
a := 10
b := 20
fmt.Println(a, b)
swap(&a, &b)
fmt.Println(a, b)
}

如果使用传值,那么修改的swap函数栈帧上开辟的变量,无法实现交换两个数。
1.3、new和make
var userinfo map[string]string
userinfo["username"] = "张三"
fmt.Println(userinfo)
执行上面的代码会引发panic,为什么呢?在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它, 还要为它分配内存空间,否则我们的值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。要分配内存,就引出来今天的new和make。Go语言中new和make是内建的两个函数,主要用来分配内存。
1、new函数分配内存。

var p1 = new(int)
*p1 = 10
fmt.Printf("p1的值: %v, p1的类型: %T, p1的地址: %p, p1指向的值: %v\n", p1, p1, &p1, *p1)
var p2 *int
p2 = new(int)
*p2 = 20
fmt.Printf("p2的值: %v, p2的类型: %T, p2的地址: %p, p2指向的值: %v\n", p2, p2, &p2, *p2)

2、make函数分配内存
make也是用于内存分配的,区别于 new,它只用于slice、 map以及channel的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型, 所以就没有必要返回他们的指针了。 make函数的函数签名如下:
func make(t Type, size ...IntegerType) Type
make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。这个我们在前面的教程中都有说明,关于 channel我们会在后续的章节详细说明。
3、new与make的区别
1、二者都是用来做内存分配的。
2、make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身。
3、而 new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
2、结构体
Golang中没有类的概念,Golang中的结构体和其他语言中的类有点相似。和其他面向对象语言中的类相比,Golang中的结构体具有更高的扩展性和灵活性。
Golang中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,Golang 提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。也就是我们可以通过struct来定义自己的类型了。
2.1、type关键字的使用
1、自定义类型
在Go语言中有一些基本的数据类型,如 string、整型、浮点型、布尔等数据类型,Go语言中可以使用 type 关键字来定义自定义类型。
package main
import "fmt"
type myInt int
type clacType func(int, int) int
func main() {
var a myInt
a = 10
var b int = 20
fmt.Printf("值: %d, 类型: %T\n", a, a)
fmt.Printf("值: %d, 类型: %T\n", b, b)
}

2、类型别名
类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。
package main
import "fmt"
type myInt int
type clacType func(int, int) int
type myFloat = float64
func main() {
var a myInt
a = 10
var b int = 20
fmt.Printf("值: %d, 类型: %T\n", a, a)
fmt.Printf("值: %d, 类型: %T\n", b, b)
var f myFloat
f = 3.1415926
fmt.Printf("值: %v, 类型: %T\n", f, f)
}

这里类似C/C++中的typedef和using对类型起别名。
2.2、结构体的定义和初始化
使用type和struct关键字来定义结构体,具体代码格式如下:

其中:
• 类型名:表示自定义结构体的名称,在同一个包内不能重复。
• 字段名:表示结构体字段名。结构体中的字段名必须唯一。
• 字段类型:表示结构体字段的具体类型。
比如定义一个Person结构体,里面保存了人的各种信息:
type Person struct {
Name string
Age int
Sex string
}
注意:结构体首字母可以大写也可以小写,大写表示这个结构体是公有的,在其他的包里面可以使用。小写表示这个结构体是私有的,只有这个包里面才能使用。
以下是结构体初始化的几种方式:
方式一:

最低0.47元/天 解锁文章
1247

被折叠的 条评论
为什么被折叠?



