go 学习笔记 - 类型

与 C# 的类型相比, go 语言的类型有两点很大的差异, 1是没有值类型和引用类型之分, 2是对象在栈上还是在堆上由运行时决定(C++中是由类的使用者决定, C#是由类的定义者决定, go与Java类似,由编译器(java 是JVM)决定分析决定)

1. go 语言中的所有类型都是值类型, 没有引用类型, 变量之间赋值与函数传参全部都是值传递
        示例:

type Persion struct { age int }
func main(){
    p1 := Person{age:33}
    p2 := p1            // 注: p2 将获得一片新分配的内存, 并将p1中的所有内容都copy了一份到其中
    p2.age = 34        // 此时的 p1.age 仍然是33
}

2. 关于数组(array), 同样是值类型的
        示例:

a1 := [3]int{1, 2, 3}
a2 := a1        // 注: a1 将被整个拷贝一份
a2[1] = 4        // 此时 a1[1] 仍然是 2

3. 关于切片(slice), 同样是值类型, 只不过内部有一根指向用于具体存放数据的array的指针
        切片的内部结构类似于

type Slice struct {
    pArray *[]int
    nLen int
    nCap int        // 当nLen增长到了nCap, pArray 会扩容(即:重新创建个更大的数组,并把数据拷贝过去)
}

        所以 当两个切片变量间赋值后, 会指向同一个数组, 但仅仅只是这样而已, 如果其中一个切片因为append发生了扩容, 将不会影响另一个
        示例:

s1 := make([]int, 4, 4)
s1[3] = 5
s2 := s1            // slice的内容被拷贝, 但s1 和 s2 中的指针 pArray 指向的是同一个 数组
s2[3] = 6            // 这时 s1[3] 也变成了 6
fmt.Println(s1[3])
fmt.Println(s2[3])
s2 = append(s2, 44)        // 因为 len将大于cap, 所以 s2 要扩容, 这将导致 s2 的 pArray 会指向一个新的 数组
s2[3] = 7            // 这时 s1[3] 仍然是 6
fmt.Println(s1[3])
fmt.Println(s2[3])

4. 但是像 slice 和 map, chan 由其特殊的地方, 首先他们是内建类型, 要正确的创建一个slice,map或chan 只能使用make
        如果单独定义 slice(包括map和chan)的变量是可以的, 但是只会对slice中的变量作零值初始化, 而且很要注意的是, 这个变量与nil比较时的结果是 true, 我的分析是, 不使用make而单独定义一个slice变量 没有什么实质性的作用, 不能对它做append等操作, 从使用的角度看与nil值没什么区别, 但len和cap函数还是能用的, 结果都是0
        示例:
    

var s1 []int                // 注: 这个时候的确是分配了 slice 的 pArray, nLen, nCap        
fmt.Println(len(s1))        // 结果是0
fmt.Println(s1 = = nil)        // 结果是 true, 因为 runtime 对 slice 类型的变量 与 nil 值比较时 做了特殊处理



        我们可以尝试强行分配一个 为 NULL 的 slice 指针
 

var s2 *[]int        // s2 的值是 nil
fmt.Println(len(s2))        // 编译不过, len 不接受 指针
fmt.Println(len(*s2))        // 编译通过, 但运行报错, 因为 s2 这地址是NULL, 无法取值



        再看另外一种现象, 使用new来创建 slice
 

s1 := new([]int)
fmt.Println(*s1 = = nil) // 结果是 true, 原因就是 虽然 slice 创建了, 但是 内容(pArray, nCap, nLen)都是零值, 
                         // 没有什么意义, runtime 对其做了特殊处理, 认为 这样的 slice 就是 nil



        可以总结一下:
        1. 对于 slice, map, chan 要正确使用的话, 必须使用 make 来创建它们, 直接定义变量或使用new都不行
        2. 其实直接定义变量和使用new, 在内存分配上没有任何区别, 只不过 一个返回的是 类型的变量, 一个返回的是类型指针变量, 置于是在栈上还是在堆上, 不是 使不使用new 来定的, 而是 go语言的编译器根据上下文场景定的
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值