比较array和slice

array和slice功能非常像, 容易搞混. 本文比较下array和slice的区别. 然后黑一下.

go版本: go version go1.13.3 linux/amd64

1. array       

        类型相同, 长度固定的数据集合. 长度无法修改
        类型为: [len]T (len不能省略)
        值是一段连续的内存区域和一些额外属性
        属性示例:
                beg_addr        # 起始地址. 赋值时固定
                len                  # 长度. 赋值时固定
                cap                 # 保存实际长度. 赋值时固定,始终和len相同
        要点:

    •  编译时需要确定长度. 需要显式指定长度, 或者使用...由编译器自动推断
    • go中都是值传递(pass by value). 因此赋值会复制数组本身.
    • 引用需要借助pointer

2. slice       

        可以看作是内存的视图,展示内存的一段区域
        类型为: []T
        值是一个指向内存的数据结构, 类似指向数组的指针
        属性示例:
                array               # 底层数组
                len                  # 长度. 赋值(包括reslice)时设置
                cap                 # 从起始地址开始,最多可以展示的内存长度. = 底层数组结束地址-起始地址
        要点:

    • 由于传递的是指针, 函数内可以修改外部slice指向的底层数组.  
    • 多个slice可以指向同一个底层数组.

3. 陷阱       

  • append slice必须赋值

                append可能会导致cap不足,需要重新分配内存. 此时需要修改slice指向的内存地址,len,cap. 但是由于append没法修改原slice变量的属性, 这就出现了矛盾: 需要修改原输入参数,但有不能产生副作用. golang的解决方法是强制规定append的返回值必须赋值给slice.算是解决了忘记赋值的问题, 但是太丑陋了. 给slice提供一个append方法不是更简单直接吗?

  • append重新分配内存导致的混乱

                多个slice可以指向同一块内存数据. 对其中一个slice(假设为A)做append后, A可能不再和其他slice共享一块内存数据.
                这种可能性受如下因素影响:
                        - slice是否满了 (len>=cap)
                                与运行时状态有关. 可能引起难以复现的bug
                        - slice增长算法的具体实现
                                可能出现不同go版本可能表现不同的诡异问题

                我觉得, 作为一个slice, 就应该保持内心的纯粹, 不能贪婪. slice应该只做视图, 不应该有改变底层大小的能力. slice应该添加只读和可写的功能. 去掉append方法. 假设按照这种方法, 看下会有哪些改进.

    • 由于slice不会修改底层数组的长度, 因此不会出现slice共享失效的问题. slice变成了纯粹了视图层.
    • 可以对数组的一段数据做操作. 比如排序,求和等等. 和现有功能一样
    • 引入只读slice后, 可以更安全的共享片段数据.
    • slice也没有必要由编译器做. 给array对象添加一个slice方法就可以了.

               而变长数组就交给vector库就可以了. 何必通过编译器做这些库应该做的那? 哦, golang不支持泛型. 难道是由于不支持泛型, 才搞出这种设计的?

猜你喜欢

转载自www.cnblogs.com/shouzhuo/p/12076686.html