为何广州市越秀区建设局网站上的WordPress文章在数据库中找不到?

摘要:广州市越秀区建设局网站,wordpress文章为啥数据库中找不到,什么静态网站容易做,微信二维码生成器Slice slice 翻译成中文就是切片,它和数组(array&am
广州市越秀区建设局网站,wordpress文章为啥数据库中找不到,什么静态网站容易做,微信二维码生成器Slice slice 翻译成中文就是切片#xff0c;它和数组#xff08;array#xff09;很类似#xff0c;可以用下标的方式进行访问#xff0c;如果越界#xff0c;就会产生 panic。但是它比数组更灵活#xff0c;可以自动地进行扩容。 了解 slice 的本质, 最简单的方法就是…Slice slice 翻译成中文就是切片它和数组array很类似可以用下标的方式进行访问如果越界就会产生 panic。但是它比数组更灵活可以自动地进行扩容。 了解 slice 的本质, 最简单的方法就是看它的源码: // runtime/slice.go type slice struct {array unsafe.Pointer // 元素指针len int // 长度 cap int // 容量 } slice 共有三个属性: 指针 指向底层数组长度 表示切片可用元素的个数也就是说使用下标对 slice 的元素进行访问时下标不能超过 slice 的长度容量 底层数组的元素个数容量 长度。在底层数组不进行扩容的情况下容量也是 slice 可以扩张的最大限度。 注意: 底层数组是可以被多个 slice 同时指向的因此对一个 slice 的元素进行操作是有可能影响到其他 slice 的。 slice 创建 方式代码示例直接声明var slice []intnewslice : *new([]int)字面量slice : []int{1,2,3,4}makeslice : make(int[], 5, 10)从切片或者数组截取slice : array[1:5] 或 slice : sourceSlice[1:5] 直接声明 第一种创建出来的 slice 其实是一个 nil slice。它的长度和容量都为0。和nil比较的结果为true。 这里比较混淆的是empty slice它的长度和容量也都为0但是所有的空切片的数据指针都指向同一个地址 0xc42003bda0。空切片和 nil 比较的结果为false。 创建方式nil切片空切片方式一var s1 []intvar s2 []int{}方式二var s4 *new([]int)var s3 make([]int, 0)len00cap00和 nil 比较truefalse nil 切片和空切片很相似长度和容量都是0官方建议尽量使用 nil 切片。 关于nil slice和empty slice的探索可以参考 - 深度解析 Go 语言中「切片」的三种特殊状态 字面量 比较简单直接用初始化表达式创建。 package mainimport fmtfunc main() {s1 : []int{0, 1, 2, 3, 8: 100}fmt.Println(s1, len(s1), cap(s1)) }运行结果 [0 1 2 3 0 0 0 0 100] 9 9 唯一值得注意的是上面的代码例子中使用了索引号直接赋值这样其他未注明的元素则默认 0 值 make make 函数需要传入三个参数切片类型长度容量。当然容量可以不传默认和长度相等。 使用 make 关键字创建 slice: packge main import fmtfunc main(){// 长度为 5, 容量为 10slice : make(int[], 5, 10)// 索引为 2 的元素赋值为 2slice[2] 2 fmt.Println(slice) } 截取 截取也是比较常见的一种创建 slice 的方法可以从数组或者 slice 直接截取当然需要指定起止索引位置。 基于已有 slice 创建新 slice 对象被称为 reslice。新 slice 和老 slice 共用底层数组新老 slice 对底层数组的更改都会影响到彼此。 基于数组创建的新 slice 对象也是同样的效果对数组或 slice 元素作的更改都会影响到彼此。 值得注意的是新老 slice 或者新 slice 老数组互相影响的前提是两者共用底层数组如果因为执行 append 操作使得新 slice 底层数组扩容移动到了新的位置两者就不会相互影响了。所以问题的关键在于两者是否会共用底层数组。 截取操作采用如下方式 data : [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // data[low, high, max] slice : data[2:4:6] 其他易错点 slice 和数组的区别在哪 slice 的底层数据是数组slice 是对数组的封装它描述一个数组的片段。两者都可以通过下标来访问单个元素。 数组是定长的长度定义好之后不能再更改。在 Go 中数组是不常见的因为其长度是类型的一部分限制了它的表达能力比如 [3]int 和 [4]int 就是不同的类型。 而切片则非常灵活它可以动态地扩容。切片的类型和长度无关。 append 到底做了什么 先来看看 append 函数的原型 func append(slice []Type, elems ...Type) []Type append 函数的参数长度可变因此可以追加多个值到 slice 中还可以用 … 传入 slice直接追加一个切片。 slice append(slice, elem1, elem2) slice append(slice, anotherSlice...)注: append函数返回值是一个新的sliceGo编译器不允许调用了 append 函数后不使用返回值。 slice 扩容 在分配内存空间之前需要先确定新的切片容量运行时根据切片的当前容量选择不同的策略进行扩容 大多数的文章都是这样描述的: 当原 slice 容量小于 1024 的时候新 slice 容量变成原来的 2 倍原 slice 容量超过 1024新 slice 容量变成原来的1.25倍。 其实结论不太准确的 为了说明上面的规律是错误的如下代码 package mainimport fmtfunc main() {s : make([]int, 0)oldCap : cap(s)for i : 0; i 2048; i {s append(s, i)newCap : cap(s)if newCap ! oldCap {fmt.Printf([%d - %4d] cap %-4d | after append %-4d cap %-4d\n, 0, i-1, oldCap, i, newCap)oldCap newCap}} }运行结果 [0 - -1] cap 0 | after append 0 cap 1 [0 - 0] cap 1 | after append 1 cap 2 [0 - 1] cap 2 | after append 2 cap 4 [0 - 3] cap 4 | after append 4 cap 8 [0 - 7] cap 8 | after append 8 cap 16 [0 - 15] cap 16 | after append 16 cap 32 [0 - 31] cap 32 | after append 32 cap 64 [0 - 63] cap 64 | after append 64 cap 128 [0 - 127] cap 128 | after append 128 cap 256 [0 - 255] cap 256 | after append 256 cap 512 [0 - 511] cap 512 | after append 512 cap 1024 [0 - 1023] cap 1024 | after append 1024 cap 1280 [0 - 1279] cap 1280 | after append 1280 cap 1696 [0 - 1695] cap 1696 | after append 1696 cap 2304在老 slice 容量小于1024的时候新 slice 的容量的确是老 slice 的2倍。目前还算正确。 但是当老 slice 容量大于等于 1024 的时候情况就有变化了。当向 slice 中添加元素 1280 的时候老 slice 的容量为 1280之后变成了 1696两者并不是 1.25 倍的关系1696/12801.325。添加完 1696 后新的容量 2304 当然也不是 1696 的 1.25 倍。 可见现在网上各种文章中的扩容策略并不正确。我们直接搬出源码源码面前了无秘密。 从前面汇编代码我们也看到了向 slice 追加元素的时候若容量不够会调用 growslice 函数所以我们直接看它的代码。 // go 1.9.5 src/runtime/slice.go:82 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 newcap cap {newcap newcap / 4}}}// ……capmem roundupsize(uintptr(newcap) * ptrSize)newcap int(capmem / ptrSize) }看到了吗如果只看前半部分现在网上各种文章里说的 newcap 的规律是对的。现实是后半部分还对 newcap 作了一个内存对齐这个和内存分配策略相关。进行内存对齐之后新 slice 的容量是要 大于等于 老 slice 容量的 2倍或者1.25倍。 之后向 Go 内存管理器申请内存将老 slice 中的数据复制过去并且将 append 的元素添加到新的底层数组中。 为什么 nil slice 可以直接 append 其实 nil slice 或者 empty slice 都是可以通过调用 append 函数来获得底层数组的扩容。最终都是调用 mallocgc 来向 Go 的内存管理器申请到一块内存然后再赋给原来的nil slice 或 empty slice然后摇身一变成为“真正”的 slice 了。 总结 切片是对底层数组的一个抽象描述了它的一个片段。切片实际上是一个结构体它有三个字段长度容量底层数据的地址。多个切片可能共享同一个底层数组这种情况下对其中一个切片或者底层数组的更改会影响到其他切片。append 函数会在切片容量不够的情况下调用 growslice 函数获取所需要的内存这称为扩容扩容会改变元素原来的位置。扩容策略并不是简单的扩为原切片容量的 2 倍或 1.25 倍还有内存对齐的操作。扩容后的容量 原容量的 2 倍或 1.25 倍。当直接用切片作为函数参数时可以改变切片的元素不能改变切片本身想要改变切片本身可以将改变后的切片返回函数调用者接收改变后的切片或者将切片指针作为函数参数。 参考 深度解密Go语言之Slice