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

为什么做不了自己的网站网站开发最严重的问题

为什么做不了自己的网站,网站开发最严重的问题,app开发公司费用,北京环球影城可以带水果吗序言 在许多开发语言中,动态数组是必不可少的一个组成部分。在实际的开发中很少会使用到数组,因为对于数组的大小大多数情况下我们是不能事先就确定好的,所以他不够灵活。动态数组通过提供自动扩容的机制,极大地提升了开发效率。这…

序言

 在许多开发语言中,动态数组是必不可少的一个组成部分。在实际的开发中很少会使用到数组,因为对于数组的大小大多数情况下我们是不能事先就确定好的,所以他不够灵活。动态数组通过提供自动扩容的机制,极大地提升了开发效率。这篇文章将介绍 Go 语言中的动态数组 — slice(切片)


1. 数据结构

 切片的组成如下, 每一个字段的含义如下:

  • Data:指向存储元素数组的指针;
  • Len:该数组中元素的个数
  • Cap:该数组的容量大小
type SliceHeader struct {Data uintptrLen  intCap  int
}

如果你之前了解过 C++ 中的 vector 你会发现其实他们的思路是一样的。一个实际存储元素的切片如下:在这里插入图片描述


2. 切片的初始化

a. 声明但不初始化

 在 Go 语言中,如果你声明一个切片但不初始化它,它的默认值是 nil。这意味着该切片没有指向任何底层数组,长度和容量都为 0:

func main() {var slice []intsliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))dataPtr := unsafe.Pointer(sliceHeader.Data)fmt.Printf("data = %v, len = %d, cap = %d\n", dataPtr, len(slice), cap(slice))
}

这里程序的输出是:

data = , len = 0, cap = 0

b. 带初始值初始化

 比起第一种方式,这个会在声明的时候带上字面值来初始化一个切片:

slice := []int{1, 2, 3} 
fmt.Printf("data = %v, len = %d, cap = %d\n", slice, len(slice), cap(slice))

此时,该切片的 lencap 会和元素数量保持一致,程序输出:

data = [1 2 3], len = 3, cap = 3

c. 使用 make 初始化

 使用 make 来初始化一个切片也有两者方式,首先是第一种:

slice := make([]int, 5)
fmt.Printf("data = %v, len = %d, cap = %d\n", slice, len(slice), cap(slice))

这代表创建一个切片,并且切片的 lencap 都是 5,切片的元素的值采用该类型的默认值:

data = [0 0 0 0 0], len = 5, cap = 5

第二种是将 lencap 分别赋值:

slice := make([]int, 2, 4)
fmt.Printf("data = %v, len = %d, cap = %d\n", slice, len(slice), cap(slice))

这代表创建一个切片,并且切片的len 是 2,cap 是 4:

data = [0 0], len = 2, cap = 4

这也是最常用的方式,使用 make 来预先分配内存大小可以避免后续添加元素时频繁进行扩容操作!

d. 下标索引初始化

Go 支持指定一个索引范围来初始化一个切片,这是 C++vector 所不具备的能力,举个例子:

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]
fmt.Println(slice)   // [2 3 4]

这里有一个长度为 5 的数组,现在使用索引范围 [1, 4) 「左闭右开」 来初始化一个切片,甚至还可以这样表达:

slice = arr[:4]   // 等价于 [0:4]
slice = arr[1:]   // 等价于 [1:len(arr) - 1]

现在有一个问题,使用索引初始化的切片和原数组是什么关系呢?换句话说这里是否涉及到了深拷贝呢?上代码:

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2, 3, 4]
slice[0] = 0 // 修改值
fmt.Printf(“arr=%v\n”, arr)
fmt.Printf(“slice=%v\n”, slice)

输出结果是:

arr=[1 0 3 4 5]
slice=[0 3 4]

上文中我们了解到了一个 slice 的结构是怎么样的,结合输出的结果,不难推断出 data 指针指向了该数组的第二个位置,如下:
在这里插入图片描述
这里 cap 的大小为什么是 4 怎么得到的呢 — cap = cap(arr) - 1


3. 切片的追加和扩容

a. 元素追加

 我们可以通过 append 操作来在切片最后追加元素,追加方式也有多种,举个栗子:

slice := []int{1, 2}
slice = append(slice, 3)       // 追加一个元素
slice = append(slice, 4, 5, 6) // 追加多个元素
slice = append(slice, []int{7, 8}...) // 追加一个切片,...表示解包,不能省略

对于追加的操作,大家是否存在疑惑的点呢?我刚开始就不理解为什么在追加操作后对 slice 进行赋值的操作。这是因为 append 函数有一个重要的特性需要特别注意:它可能会返回一个新的底层数组(取决于是否进行扩容操作)。如果没有进行赋值操作,那么 slice 还是指向原来的数组,举个栗子:

在这里插入图片描述

b. 切片扩容

 当切片的 len 等于 cap 时,在下一次 append 操作前就会进行一次扩容操作,扩容的逻辑如下:

func growslice(et *_type, old slice, cap int) slice {...newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.len < 1024 {newcap = doublecap} else {for 0 < newcap && newcap < cap {newcap += newcap / 4}if newcap <= 0 {newcap = cap}}}...
}

扩容的策略总结如下:
在这里插入图片描述
可以看到 Go 语言增长容量的策略还是比较缓和的。


4. 切片易踩的坑

a. 参数传递类型傻傻分不清

 首先,我们先聊聊 C++ 当中的值传递和引用传递,就比如:

int main() {vector<int> vec = { 1, 2, 3, 4, 5 }funcJustForRead(vec)return 0;
}void funcJustForRead(vector<int> &vec) {...
}

对于某些只读的场景,我们一般会传引用,这样就大大减少了拷贝带来的开销。在 Go 语言中好像并没有 引用 的概念?但是仔细思考一下,Go 真的需要吗:

func main() {slice := []int{ 1, 2, 3, 4, 5 }funcJustForRead(slice)
}func funcJustForRead(slice []int) {...
}

形参是实参的拷贝,slice 中指向元素的是 data 指针,即使形参和实参的 data 不一样,但是两者是指向的同一个数组,所以不需要引用。
 现在,这里有一个函数会对切片进行追加操作,我依然是值传递是否还是可行呢?举个栗子(假设这里不涉及扩容操作):

func main() {slice := []int{1, 2}fmt.Println(slice)funcForAppend(slice)fmt.Println(slice)
}func funcForAppend(slice []int) {slice = append(slice, 3)
}

输出结果是:

[1 2]
[1 2]

并没有预想的新增一个值,为什么?上面我们介绍了,append 会返回一个新的切片,我们在 main 中使用的还是原来的切片。怎么解决呢?传递指针:

func main() {slice := make([]int, 0, 2)fmt.Println(slice)funcForAppend(&slice)fmt.Println(slice)
}func funcForAppend(slice *[]int) {*slice = append(*slice, 3)
}

b. len 和 cap 傻傻分不清

 之前我们谈到过,可以预先分配好空间,可以避免后续的频繁扩容操作,但是是否会有以下的误解呢:

func main() {slice := make([]int, 5)slice = append(slice, 1)slice = append(slice, 1)slice = append(slice, 1)slice = append(slice, 1)fmt.Println(slice) // [0 0 0 0 0 1 1 1 1]
}

这里代表预先分配好 5 个空间,并且每一个空间使用该类型的默认值填充,当我们新加入元素时,是在已有的基础上往后添加而不是从前开始覆盖。正确的姿势应该是这样子的:

func main() {slice := make([]int, 0, 5)slice = append(slice, 1)slice = append(slice, 1)slice = append(slice, 1)slice = append(slice, 1)fmt.Println(slice) // [1 1 1 1]
}

5. 总结

 不仅只是会使用,并且知其所以然。我自认为这是非常重要的,这不仅能够很大程度上减小我们在开发中犯错的概念,还能够有效提升代码的质量。所以通过这篇 silce 带我们走入 Go 的世界吧。


文章转载自:

http://Odce29tV.zfyfy.cn
http://eiFiXjXi.zfyfy.cn
http://TaRYmLwv.zfyfy.cn
http://I6zjQTNo.zfyfy.cn
http://UsydUYFT.zfyfy.cn
http://tZLyBNpA.zfyfy.cn
http://Ki1nIMgu.zfyfy.cn
http://r5uUNwvG.zfyfy.cn
http://RHcpUjKh.zfyfy.cn
http://MZBBjsxY.zfyfy.cn
http://JUpCIviI.zfyfy.cn
http://9nfqVU3i.zfyfy.cn
http://xaWFCwea.zfyfy.cn
http://DOUSS7Ky.zfyfy.cn
http://lW8Z3lTM.zfyfy.cn
http://gWhvfTv0.zfyfy.cn
http://uWv4DImc.zfyfy.cn
http://0ZigaWYS.zfyfy.cn
http://g6srvVOX.zfyfy.cn
http://xuEo1BcX.zfyfy.cn
http://AarK8DIf.zfyfy.cn
http://YjiMTd1f.zfyfy.cn
http://jTWs0iku.zfyfy.cn
http://UQhZsTNh.zfyfy.cn
http://vUwMpZM0.zfyfy.cn
http://ex6uv9tP.zfyfy.cn
http://Fl44VoR5.zfyfy.cn
http://4TJg4sGw.zfyfy.cn
http://W1oYPWBz.zfyfy.cn
http://Rvuix4zX.zfyfy.cn
http://www.dtcms.com/wzjs/648587.html

相关文章:

  • 网站建设的流程简答题招远网站建设价格
  • 网站服务器租赁哪家好江苏元鼎建设工程有限公司网站
  • 网站建设邮如何使用家里电脑做网站服务器
  • 做网站要学什么杭州有名的纯设计公司
  • 深圳设计网站公司哪家好wordpress slug translate
  • 宁波网站推广找哪家公司怎么才能百度做网站
  • asp网站数据库连接软件开发学什么专业好
  • 网站备案流程阿里云微信公众号微信公众平台
  • win2008sr怎么用iis做网站大宗交易平台有哪些
  • 网站开发和桌面开发哪个难网站主题模板制作
  • 专做热血电影的网站wordpress多域名不稳定
  • 做一个flash网站多少钱wordpress询盘功能
  • 海珠网站建设哪家好东莞常平镇地图全图
  • 网站开发建设技术规范书全屏网站设计技巧
  • 烟台个人网站建设凡客网登录
  • 完成网站群建设学校网站建设设想
  • 中国建设规划采购网站wordpress添加视频插件
  • 网络公司网站开发案例外国网站快速申请qq
  • 湖北网站建设路公司彩页设计制作
  • 网站建设的用户名和密码代码南昌seo站外优化
  • 网站服务器备案查询网站做网站页面遇到的问题
  • 站长音效早期网页游戏
  • 自学建立网站常见cms网站源码下载
  • 网站内怎么做链接虚拟币交易网站建设
  • 怎么做网站信任网站建设 图纸网
  • 最好最值得做的调查网站chrome官网
  • 专业营销的网站建设公司哪家好静态网页托管
  • 东莞网站建设 织梦什么是企业文化
  • 自己做的网站出现iis7常见制作网页的软件
  • 网站用品推广网页wordpress 热门主题