当前位置: 首页 > news >正文

永安网站建设/优化视频

永安网站建设,优化视频,销售渠道及方式,网站后台栏目根据什么做的本文目录1、数组1.1、声明方式1.2、数组赋值于复制1.3、编译时结构1.4、编译时内存优化2、切片2.1、切片声明2.2、切片的数据结构2.3、切片截取2.4、切片赋值于复制2.5、切片的扩容公原理2.6、make初始化2.7、切片的内存逃逸1、数组 1.1、声明方式 // 声明 var arr1 [3]int /…

本文目录

    • 1、数组
      • 1.1、声明方式
      • 1.2、数组赋值于复制
      • 1.3、编译时结构
      • 1.4、编译时内存优化
    • 2、切片
      • 2.1、切片声明
      • 2.2、切片的数据结构
      • 2.3、切片截取
      • 2.4、切片赋值于复制
      • 2.5、切片的扩容公原理
      • 2.6、make初始化
      • 2.7、切片的内存逃逸

1、数组

1.1、声明方式

// 声明
var arr1 [3]int
// 声明并初始化
arr2 := [3]int{1,2,3}
// 声明并初始化,编译时长度自动推断
arr3 := [...]int{1,2,3,4}// arr1: [3]int, arr2: [3]int, arr3: [4]int
fmt.Printf("arr1: %T, arr2: %T, arr3: %T\n", arr1, arr2, arr3)

1.2、数组赋值于复制

func main() {a := [3]int{1, 2, 3}fmt.Printf("a array address is : %p\n", &a)// 作为参数传递testArrayCopy(a)// 赋值c := afmt.Printf("c array address is : %p\n", &c)
}func testArrayCopy(b [3]int) {fmt.Printf("b array address is : %p\n", &b)
}// 输出结果:
// a array address is : 0xc00000c150
// b array address is : 0xc00000c180
// c array address is : 0xc00000c198

1.3、编译时结构

// Array contains Type fields specific to array types.
type Array struct {Elem  *Type // element typeBound int64 // number of elements; <0 if unknown yet
}

不难看出,编译阶段记录了保存的元素以及元素的数量

1.4、编译时内存优化

值得注意的是:在编译阶段,会将长度小于4的数组放在栈中,大于4的则会放在静态只读区中,下面通过一个例子来验证一下

示例代码:

func testArrayAddress() {arr1 := [3]int{1, 2, 3}arr2 := [3]int{1, 2, 3}arr3 := [3]int{1, 2, 3}arr4 := [3]int{1, 2, 3}fmt.Printf("arr1: %p, arr2: %p, arr3: %p, arr4: %p\n", &arr1, &arr2, &arr3, &arr4)
}
// 第一次执行,输出
// arr1: 0xc00000c150, arr2: 0xc00000c168, arr3: 0xc00000c180, arr4: 0xc00000c198// 修改代码后再执行一次
func testArrayAddress() {arr1 := [3]int{1, 2, 3}arr2 := [3]int{1, 2, 3}arr3 := [5]int{1, 2, 3}arr4 := [3]int{1, 2, 3}fmt.Printf("arr1: %p, arr2: %p, arr3: %p, arr4: %p\n", &arr1, &arr2, &arr3, &arr4)
}
// 第二次执行,输出
// arr1: 0xc00000c150, arr2: 0xc00000c168, arr3: 0xc00000a390, arr4: 0xc00000c180

对比两次输出的结果不难看出,arr3的长度从3变成5之后,其内存地址就不再和arr1arr2arr4紧挨着了,也就是没有被分配在栈中了

2、切片

相比较数组,切片的长度是可变的,因此也不需要指定长度

2.1、切片声明

func main() {var s1 []int                // 长度和容量默认都是0var s2 = []int{1, 2, 3}     // 长度和容量都为元素个数var s3 = make([]int, 5)     // 长度和容量都为5var s4 = make([]int, 5, 10) // 长度为5,容量为10// s1: []int, s2: []intfmt.Printf("【切片类型】 s1: %T, s2: %T, s3: %T, s4: %T\n", s1, s2, s3, s4)fmt.Printf("【元素数量】 s1: %d, s2: %d, s3: %d, s4: %d\n", len(s1), len(s2), len(s3), len(s4))fmt.Printf("【切片容量】 s1: %d, s2: %d, s3: %d, s4: %d\n", cap(s1), cap(s2), cap(s3), cap(s4))
}// 输出
// 【切片类型】 s1: []int, s2: []int, s3: []int, s4: []int
// 【元素数量】 s1: 0, s2: 3, s3: 5, s4: 5 
// 【切片容量】 s1: 0, s2: 3, s3: 5, s4: 10

2.2、切片的数据结构

// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type SliceHeader struct {Data uintptrLen  intCap  int
}

SliceHeader是切片在运行时的表示,内部有指向真正的数据结构的指针、当前长度和切片容量

2.3、切片截取

下面是切片截取的简单代码示例:

func testCutOut() {nums1 := []int{1, 2, 3, 4, 5, 6, 7}nums2 := nums1[2:4] // 截取区间 [2,4)fmt.Printf("nums2: %v\n", nums2)
}

下面是图解和示例:

func testCutOut() {nums1 := []int{1, 2, 3, 4, 5, 6, 7}nums2 := nums1[2:4] // 截取区间 [2,4)fmt.Printf("nums2: %v\n", nums2)// 修改nums2nums2[0] = 0nums2[1] = 0// 输出 nums1: [1 2 0 0 5 6 7]fmt.Printf("nums1: %v\n", nums1)
}

在上面的例子中nums2截取自nums1,修改了nums2后,nums1也发生了变化,说明他们所指向的是同一份数据,再结合前面说到过的切片的数据结构,如下图所示:

image.png

2.4、切片赋值于复制

func testSliceCopy() {nums1 := []int{1, 2, 3, 4, 5, 6, 7}nums2 := nums1nums2[0] = 100// 输出 nums1: [100 2 3 4 5 6 7]fmt.Printf("nums1: %v\n", nums1)
}

切片的赋值于切片的截取一样,本质上都是值赋值,这里说的值复制指的是对SliceHeader 结构体的值复制,就作用而言,又与引用传递没什么区别,指向的是同一份数据

2.5、切片的扩容公原理

切片扩容的源码在runtime/slice.go中的,核心代码如下所示:

func growslice(et *_type, old slice, cap int) slice {newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.cap < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}}}

解释一下上面源码的逻辑:

这里为了方便解释,以oldcap代表旧长度,以newcap代表新长度

  • 首先如果newcap > oldcap * 2,则最终的长度为newcap
  • 如果newcap < 1024 && newcap < oldcap * 2,则新的长度为oldcap * 2
  • 如果newcap > 1024,则新长度为oldcap + oldcap / 4

通俗的说,如果新的容量大于原来的2倍,则不作特殊处理,否则根据新容量是否大于1024来选择进行2倍扩容,否则不断扩容1/4直至满足为止。

2.6、make初始化

runtime/slice.go文件中还有两个方法,分别是makeslicemakeslice64,当我们使用make对切片进行初始化的时候,便会调用这两个方法中的其中一个。这两个的区别分别是什么呢?以及调用的时机是怎样的?下面我们仔细分析:

makeslice源码:

func makeslice(et *_type, len, cap int) unsafe.Pointer {mem, overflow := math.MulUintptr(et.size, uintptr(cap))if overflow || mem > maxAlloc || len < 0 || len > cap {mem, overflow := math.MulUintptr(et.size, uintptr(len))if overflow || mem > maxAlloc || len < 0 {panicmakeslicelen()}panicmakeslicecap()}return mallocgc(mem, et, true)
}

makeslice64源码:

func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {len := int(len64)if int64(len) != len64 {panicmakeslicelen()}cap := int(cap64)if int64(cap) != cap64 {panicmakeslicecap()}// 最终还会调用makeslicereturn makeslice(et, len, cap)
}

通过比对会发现,makeslice方法会去判断mem > maxAlloc,这里是mem指的是切片的大小,而maxAlloc,我们按住Ctrl+鼠标左键找到它,会看到下面的源码,简单翻译一下,可以知道,这是理论上能分配的最大空间,在64位系统上,理论上可以分配1<<32bit的内存,也就是4GB

// maxAlloc is the maximum size of an allocation. On 64-bit,
// it's theoretically possible to allocate 1<<heapAddrBits bytes. On
// 32-bit, however, this is one less than 1<<32 because the
// number of bytes in the address space doesn't actually fit
// in a uintptr.
maxAlloc = (1 << heapAddrBits) - (1-_64bit)*1

cmd/compile/internal/walk/builtin.go文件中的walkMakeSlice方法中有下面这样一段注释:

// n escapes; set up a call to makeslice.
// When len and cap can fit into int, use makeslice instead of
// makeslice64, which is faster and shorter on 32 bit platforms.

2.7、切片的内存逃逸

cmd/compile/internal/ir/cfg.go中存在一个变量MaxImplicitStackVarSize ,这是切片分配在栈中的最大大小,默认是64KB,换句话说,如果通过make进行初始化时候切片大小大于64KB则会发送内存逃逸,最终这个切片会被分配到堆中

// maximum size of implicit variables that we will allocate on the stack.
//   s := make([]T, n)    allocating [n]T on the stack
// Note: the flag smallframes can update this value.
MaxImplicitStackVarSize = int64(64 * 1024)
http://www.jmfq.cn/news/5194765.html

相关文章:

  • 二维码生成器免费版/青岛seo整站优化哪家专业
  • 安卓做网站/百度网首页
  • 可以仿做网站吗/网店代运营公司
  • 企业网站建设的基本流程/外贸接单网站
  • python flask做网站/bt磁力猪
  • 如何用dw做网站首页/网站流量排名
  • 网站建设公司浩森宇特/aso网站
  • 网站开发工作总结/百度站长工具怎么查排名
  • 如何做关于橱柜网站/推广怎么推
  • 微信做兼职什么网站好/金华seo全网营销
  • 网站建设插入图片怎么加/怎么建个人网站
  • 鄂州第一官方网站/亚马逊查关键词搜索量的工具
  • 用phpcms做的网站模板/如何查询百度搜索关键词排名
  • 结合七牛云 做视频网站/报个电脑培训班要多少钱
  • 卖东西的网站怎么建设/外国搜索引擎登录入口
  • 中国人做跨电商有什么网站/公司做网站推广
  • 做网站要求高吗/seo优化交流
  • 阿里巴巴网站中详情页怎么做/2022最近热点事件及评述
  • 苏州网站建设哪个比较牛/成都网站建设方案推广
  • 珠海网站建设培训学校/seo人人网
  • 经营性网站备案要求/百度账号个人中心
  • 如何创建个人app/东莞百度seo在哪里
  • 中国商标注册网/系统优化大师
  • 易语言做网站客户端/免费代理上网网站
  • 站长论坛/软文接单平台
  • 建筑工程网络图片/aso优化费用
  • 怎么用云主机做网站/淘宝关键词优化怎么弄
  • 合肥百度 网站建设/代发百度首页排名
  • 益阳网站建设公司/重庆森林在线观看
  • 企业做网站有发展么/长春今日头条新闻