security center启动类型更改不了_建造大房子,那当然要用复合数据类型了

所谓复合数据类型,就是由多种基本数据类型组合而成的功能更强大的类型,就像原子构成分子一样,复合数据类型在我们的程序会承担更繁杂的工作。

Go 语言里常用的数据类型有四种:分别是数组、slice(切片)、map(集合)以及结构体。其中,数组和结构体的长度在定义的时候就固定好了,而 slice 和 map 可以实现动态增长。

数组

数组是具有固定长度,且拥有零或多个相同数据类型元素的序列。数组元素通过它的索引来访问,索引的值从 0 到 数组长度减 1,即 [0,len(arr) - 1] (Go 内置函数 len 可以返回数组中的元素个数)。

 var arr [3]int // 声明一个int类型的数组,长度为3fmt.Println(arr[len(arr)-1]) // 输出最后一个元素,即arr[2],此处为0for i, v := range arr {    fmt.Printf("arr[%d]=%d ", i, v) // range循环,输出索引和元素}for _, v := range arr {    fmt.Printf("%d ", v) // 输出数组元素:0 0 0}

如上述所示,一个新数组的初始值为元素类型的零值。对于 int 类型来说,就是 0。所以,我们在定义时,初始化数组:

 var arr [3]int = [3]int{1, 2, 3} // 声明一个数组,赋值为{1, 2, 3}var arr2 [3]int = [...]int{1, 2, 3} // 也可用省略号代替赋值数组的长度fmt.Printf("%T", arr2) // 输出数组的类型,此处为 "[3]int"

从这个例子中我们可以看到,数组的类型包含了数组的长度。也就是说,[3]int 和 [4]int 不是一种数据类型:

 arr := [3]{1, 2, 3}arr2 := [4]{1, 2, 3, 4}arr = [4]int{1, 2, 3, 4} // 编译错误,不同数据类型之间不可赋值fmt.Println(arr == arr2) // 编译错误,不同数据类型之间不可比较

初始化数组时,我们可以根据索引去确定每个元素的值,索引可以任意顺序出现,也可以省略:

 arr := [...]string{1:"b", 0:"a", 3:"c"}fmt.Println(len(arr), arr)// 4 [a b   c],索引2被省略了,故arr[2]为空字符串

数组初始化之后,若要对其内部元素值进行修改,可以单独赋值,比如:

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

但若是想全部更改呢?我们或许可以想到将数组放到函数中处理,但是,当数组作为函数入参时,每个传入的参数都会创建一个副本,然后对副本进行处理,此时原有的数组会维持不变。所以,我们要将传入函数时,需要用到指针:

 func zero(p *[4]int) {    for i := range p {        p[i] = 0}}func main(){    arr := [...]int{1, 2, 3, 4}    zero(&arr)    fmt.Println(arr) // [0 0 0 0]}

观察上面的例子,我们可以看到 zero 函数接收的入参是一个长度为 4 的 int 数组指针。而前面已经说到,不同长度的数组,数据类型是不一样的。所以,当数组的长度发生变化时,这个函数就不能使用了。

这就是数组天然的限制——不可动态扩容。

因此,除非特殊场景,我们在开发工作中很少会使用数组,而选择长度可动态增长的 slice 切片。

slice

slice 切片是一个可动态增长的序列,和数组一样,它内部的元素都是相同类型的。slice 是一种建立在底层数组之上的轻量级数据结构,可以访问底层数组的部分或者全部元素。

slice 有三个属性:指针、长度和容量,Go 的内置函数 len 和 cap 用来返回 slice 的长度和容量:

eb60e27a572768f55c64edfc836ba699.png

一个底层数组可以对应多个 slice,这些 slice 可以引用数组的任意位置,彼此元素间还可以重叠。slice 中的操作符 s[i:j](其中 0<= i <= j <= cap(s)),会返回一个新的引用,这个引用指向索引从 i 到 j 的所有元素(左闭右开:即包含 i 元素,不包含 j 元素)。注意:如果省略了 i,则默认 i = 0;如果省略了 j,则默认 j = len(s):

 a := []int{0, 1, 2, 3, 4}b := a[1:3]fmt.Println(b)// [1 2]b = a[:3]// [0 1 2]b = a[2:]// [2 3 4]

因为 slice 本质上是对底层数组的引用,所以当 slice 传递给一个函数时,可以在函数内部直接修改底层数组的元素:

 func update(s []int) {    s[0] == 100}func main() {    arr := []int{0, 1}    update(arr)    fmt.Println(arr)// [100 1]}

Go 语言中的 append 函数可以将元素追加到 slice 后面,这个函数对于 slice 长度的动态变化很重要。

 a := []int{1, 2}a = append(a, 3)// 直接将int元素追加到slice中fmt.Println(a)// [1 2 3]b := []int{4, 5}a = append(a, b...)// 将切片b追加到a中fmt.Println(a)// [1 2 3 4 5]

除了常用的追加功能,append 可以很方便地实现删除当前元素的功能:

 a := []int{1, 2, 3, 4}i := 2a = append(a[:i], a[i+1:]...)// 删除第i个元素fmt.Println(a)// [1 2 4]
map

散列表在开发过程中的用途非常广泛,它是一个拥有键值对(key-value)元素的无序集合。这个集合里面,键是唯一的,所以键对应的值可以通过键来获取、更新或者移除。

Go 语言里,map 是散列表的引用,map 的数据类型为 map[K]V。其中 K 和 V 是键和值对应的数据类型,K 的数据类型必须是可以通过操作符 == 进行相等比较的数据类型,比如:基本数据类型 int、string 或 bool;而 V 不仅可以是基本数据类型,也可以是复杂数据类型数组、切片等。

创建一个 map:

 ages := make(map[string]int)

对 map 进行赋值:

 // 初始化时赋值ages := map[string]int {    "zhangs": 18,    "lis": 20, // 此处逗号不可省略}// 初始化后赋值ages["wangw"] = 19

在 map 中查找键为 "zangmz" 的元素:

 age ,ok := ages["zangmz"]if !ok {    fmt.Println("张麻子的年龄没有存入 map 中!")}else {    fmt.Println("张麻子的年龄为:", age)}

对 map 进行查询时,第一个元素 age 是元素返回值,二个元素是一个 bool 值,用来判断该元素是否存在,Go 语言中一般将变量名取为 ok。

上面代码中 1 到 4 行我们可以将两条语句合并成一条:

 if age, ok := ages["wangmz"]; !ok {    ... } else {    ... }

当需要从 map 中移除一个元素时,用内置函数 delete 实现:

 delete(ages, "zhangs")
结构体

结构体是将零或多个任意类型的命名变量组合在一起的数据类型,Go 语言里的结构体可以理解为其他语言中的对象,但是它的用法相对较简单,且没有复杂的继承概念。首先,我们来定义一个 Person 结构体:

 type Person struct {    Name string    Age  int}

type 和 struct 是定义结构体的两个关键字。Person 为结构体变量名,姓名 Name 和年龄 Age 是结构成员,它们可以用点号(.)的方式访问:

 var p Personp.Name = "zhangs"p.Age = 18

或者

 p := Person{        "zhangs",        18,}

从上我们不难看出,第二种赋值方式,变量的声明顺序需要和结构成员的位置保持一致。

如果结构体的名字是大写字母开头,那么这个结构体就是可导出的。换句话说,结构体名首字母的大小写,决定了该结构体的访问权限。其实成员变量也一样,不过我们在开发中,一般都将结构体和成员的访问保持一致。比如 Person 例子中首字母 P 是大写的,其他包(关于包的概念后续会补充讲解)就可以访问这个结构体和它的成员变量。

匿名成员

前面我们说到了,结构体可以包含任意类型的成员变量。所以,接下来的例子,你将看到我们在开发中常常遇到的结构体组合:

 type Point struct {    X, Y int}type Circle struct {    Center Point    Redis int}type Wheel struct {    Circle Circle    Spokes int}

上面的程序中有三个结构体,分别代表了一个带有 X 和 Y 的点坐标,一个带有圆心和半径的圆盘,以及一个带有圆盘和车轴的轮子。不难发现,它们之间有嵌套的关系。这时,我们想初始化 Wheel 的变量时就变得麻烦了:

 var w Wheelw.Circle.Center.X = 6w.Circle.Center.Y = 6w.Circle.Redis = 5w.Spokes = 20

但是,Go 提供了一种简便方法,允许我们定义只需要指定类型,而不带名称的结构体成员,即匿名成员。嵌套关系的访问,可以省略中间的匿名成员。于是,我们的代码可以这样修改一下:

 type Circle struct {    Point // 省略了变量的名字    Redis int}type Wheel struct {    Circle // 省略了变量的名字    Spokes int}

此时,访问 Wheel 的变量可以简化为:

 var w Wheelw.X = 6 //等价于 w.Circle.Center.X = 6w.Y = 6w.Redis = 5w.Spokes = 20
总结

我们在建造房子时,几乎很难精细地考虑到要用到多少砖头、瓦块或者金属;但是对窗户、大门等复杂构造模块的数量是可以有大概预估的,基本数据类型和复杂数据类型也是一样。

一门编程语言学习之初,不应纯粹追求对语法的熟练度;而是在需要的时候,可以知道有这种应用场景,能快速地查询到使用的方法。毕竟复制粘贴的工作做多了,也就熟练了(/手动狗头)。

又是一年 12 月,砖也没那么烫手了。我希望,你们向往的冬季,是温暖的火炉与暖气,是北方人眼前的银装素裹,是南方人梦里的风和日丽。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值