go 学习笔记 - 类型

本文详细探讨了Go语言的类型特性,与C#等语言对比,重点介绍了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语言的编译器根据上下文场景定的
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值