Go语言学习(八)| 类型、指针

mervyn 2018年7月15日12:45:57编程语言 GoGo语言学习(八)| 类型、指针已关闭评论132

目录

指针

Go 有指针,但是没有指针运算。你不能用指针变量遍历字符串的各个字节。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

通过类型作为前缀来定义一个指针 * 。 例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

var p *int

现在 p 是一个指向整数值的指针。 变量名和 * 之间要有个空格。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

所有新定义的变量都被赋值为其类型的零值,而指针也一样。一个新定义的或者没有任何指向的指针,有值 nil 。 在其他语言中,这经常被叫做 空(NULL)指针 ,在 Go 中就是 nil 。让指针指向某些内容,可以使用取址操作符 &文章源自编程技术分享-https://mervyn.life/1d33d77f.html

  • 如果一个 method 的 receiver 是 *T ,你可以在一个 T 类型的实例变量 V 上面调用这个 method,而不需要 &V 去调用这个 method
  • 如果一个 method 的 receiver 是 T ,你可以在一个*T 类型的变量P上面调用这个 method,而不需要 *P 去调用这个 method

例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
func main(){
    var p *int
    fmt.Printf("%v\n", p)
    var i int
    p = &i
    fmt.Printf("%v\n", p)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

<nil>
0x18600118

例2:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
func main(){
    var p *int
    var i int
    p = &i
    *p = 8
    fmt.Printf("%v\n", *p)
    fmt.Printf("%v\n", i)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

8
8
因为Go没有指针运算,所以如果这样写:*p++,它表示(*p)++:首先获取指针指向的值,然后对这个值加一。

自定义类型

Go 允许定义新的类型,通过保留字 type 实现,例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

type foo int

创建了一个新的类型 foo 作用跟 int 一样。创建更加复杂的类型需要用到 struct 保留字。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
type NameAge struct {
    name string        //不导出
    age int            //不导出
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    fmt.Printf("%v\n", a)
    fmt.Printf("%v\n", a.name)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

&{Pete 42}
Pete

struct 声明

注意 struct{} 中首字母大写的字段可以被导出, 也就是说,在其他包中可以进行读写。 字段名以小写字幕开头是当前包的私有的。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

例2:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

type person struct {
    name string
    age int
}
var P person  // P现在就是person类型的变量了

P.name = "Astaxie"  // 赋值"Astaxie"给P的name属性.
P.age = 25          // 赋值"25"给变量P的age属性
fmt.Printf("The person's name is %s", P.name)  // 访问P的name属性.

除了上面这种P的声明使用之外,还有几种声明使用方式文章源自编程技术分享-https://mervyn.life/1d33d77f.html

  • 按照顺序提供初始化值文章源自编程技术分享-https://mervyn.life/1d33d77f.html

    P := person{"Tom", 25}
  • 通过 field:value 的方式初始化,这样可以任意顺序(以这种方式初始化的时候每个字段都要写出来)文章源自编程技术分享-https://mervyn.life/1d33d77f.html

    P := person{age:24, name:"Tom"}
  • 通过 new 函数分配一个指针,此处 P 的类型为 *person文章源自编程技术分享-https://mervyn.life/1d33d77f.html

    p := new(person)

例3:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"

// 声明一个新的类型
type person struct {
    name string
    age int
}

// 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
// 注意:struct也是传值的
func Older(p1, p2 person) (person, int) {
    if p1.age>p2.age {  // 比较p1和p2这两个人的年龄
        return p1, p1.age-p2.age
    }
    return p2, p2.age-p1.age
}

func main() {
    var tom person
    // 赋值初始化
    tom.name, tom.age = "Tom", 18

    // 两个字段都写清楚的初始化
    bob := person{age:25, name:"Bob"}

    // 按照struct定义顺序初始化值
    paul := person{"Paul", 43}

    tb_Older, tb_diff := Older(tom, bob)
    tp_Older, tp_diff := Older(tom, paul)
    bp_Older, bp_diff := Older(bob, paul)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        tom.name, bob.name, tb_Older.name, tb_diff)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        tom.name, paul.name, tp_Older.name, tp_diff)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        bob.name, paul.name, bp_Older.name, bp_diff)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

Of Tom and Bob, Bob is older by 7 years
Of Tom and Paul, Paul is older by 25 years
Of Bob and Paul, Paul is older by 18 years

struct 的匿名字段

我们上面介绍了如何定义一个 struct ,定义的时候是字段名与其类型一一对应, 实际上 Go 支持只提供类型, 而不写字段名的方式,也就是匿名字段,也称为嵌入字段。当匿名字段是一个 struct 的时候,那么这个 struct 所拥有的全部字段都被隐式地引入了当前定义的这个 struct。
例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"

type Human struct {
    name string
    age int
    weight int
}

type Student struct {
    Human  // 匿名字段,那么默认Student就包含了Human的所有字段
    speciality string
}

func main() {
    // 我们初始化一个学生
    mark := Student{Human{"Mark", 25, 120}, "Computer Science"}

    // 我们访问相应的字段
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His weight is ", mark.weight)
    fmt.Println("His speciality is ", mark.speciality)
    // 修改对应的备注信息
    mark.speciality = "AI"
    fmt.Println("Mark changed his speciality")
    fmt.Println("His speciality is ", mark.speciality)
    // 修改他的年龄信息
    fmt.Println("Mark become old")
    mark.age = 46
    fmt.Println("His age is", mark.age)
    // 修改他的体重信息
    fmt.Println("Mark is not an athlet anymore")
    mark.weight += 60
    fmt.Println("His weight is", mark.weight)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

His name is  Mark
His age is  25
His weight is  120
His speciality is  Computer Science
Mark changed his speciality
His speciality is  AI
Mark become old
His age is 46
Mark is not an athlet anymore
His weight is 180

方法

可以对新定义的类型创先函数以便操作,可以通过两种途径:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

  1. 创建一个函数接受这个类型的参数。
    func doSomething(in1 *NameAge, in2 int) { /* ... */ }

    你可能已经猜到了,这其实就是函数调用。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

  2. 创建一个工作在这个类型上的函数:
    func (in1 *NameAge) doSomething(in2 int) { /* ... */ }

    这是方法调用,可以类似这样使用:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

    var n *NameAge
    n.doSomething(2)

例1:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
type NameAge struct {
    name string
    age int
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    doSomething(a, 3)
}
func doSomething(in1 *NameAge, in2 int){
    fmt.Printf("%v\n", in1.name)
    fmt.Printf("%d\n", in2)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

Pete
3

例2:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
type NameAge struct {
    name string
    age int
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    a.doSomething(333)
}
func (in1 *NameAge) doSomething(in2 int){
    fmt.Printf("%v\n", in1.name)
    fmt.Printf("%d\n", in2)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

Pete
333

类型转换

有时需要将一个类型转换为另一个类型。在 Go 中可以做到,不过有一些规则。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

注意 Go 的字符串是 UTF-8 编码的,一些字符可能是 1、2、3 或者 4 个字节结尾。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
func main(){
    mystring := "hello world"
    byteslice := []byte(mystring)
    for _, val := range byteslice {
        fmt.Printf("%v\n", val)
    }
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

104
101
108
108
111
32
119
111
114
108
100

例2文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
func main(){
    mystring := "hello world"
    byteslice := []rune(mystring)
    for _, val := range byteslice {
        fmt.Printf("%c\n", val)
    }
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

h
e
l
l
o

w
o
r
l
d

对于数值,定义了下面的转换:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

  • 将整数转换到指定的(bit)长度:uint8(int);
  • 从浮点数到整数:int(float32)。这会截断浮点数的小数部分;
  • 其他的类似:float32(int)。
    例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html
    package main
    import "fmt"
    func main(){
    myfloat := 123.72
    myint := int(myfloat)
    fmt.Printf("%.3f\n", myfloat)
    fmt.Printf("%d\n", myint)
    }

    输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

    123.720
    123

用户定义类型的转换文章源自编程技术分享-https://mervyn.life/1d33d77f.html

如何在自定义类型之间进行转换?这里创建了两个类型 foo 和 bar ,而 bar 是 foo 的一个别名:
例:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

package main
import "fmt"
type foo struct { int }        //匿名字段
type bar foo
func main(){
    b := bar{1}                //声明b 为bar 类型,并赋初值
    var f foo = b
    //上面赋值b 到f,这会引起一行错误cannot use b (type bar) as type foo in assignment(不能使用b(类型bar)作为类型foo 赋值)
    var f foo = foo(b)        //这个是可以的
    fmt.Printf("%d\n", f)
}

输出结果:文章源自编程技术分享-https://mervyn.life/1d33d77f.html

{1}

注意:转换 b 到 int 同样会出错;整数与有整数字段的结构并不一样。文章源自编程技术分享-https://mervyn.life/1d33d77f.html

weinxin
我的微信公众号
微信扫一扫
mervyn
python多版本及依赖包管理 Python

python多版本及依赖包管理

本文主要讲述如何通过 pyenv 来管理不同版本的 python ,以及如何使用 Pipenv 在同一个python版本实现项目之间依赖包的隔离。 pyenv Linux下安装 curl https:...
PHP 将16进制字符转换成汉字 PHP

PHP 将16进制字符转换成汉字

项目代码提供给外部的api,有些参数是中文的。发现有些客户在请求接口的时候,参数的值被转成了16进制,从而导致接口无法正常解析。 此时可以采用如下方法进行转移: <?php $param = &...
Go 方法指针接收者和值接收者 Go

Go 方法指针接收者和值接收者

Go 语言可以给自定义的类型添加一个方法。这里的方法其实也是函数,跟函数的区别在于在 func 关键字和函数名中间增加了一个参数,可以认为该类型也作为了参数传递入了函数中,例: package mai...