Golang基础随手笔记

        因为是随手记,所以内容是断断续续的(也是个人发文章习惯,造成视觉不适,请谅解),但是都是个人认为的主要知识点。

GO发展

三位创始人:Ken Thompson、Robert Griesemer、Rob Pike。

诞生时间:2007年开始设计,2009年趋于稳定。

设计起因:在google时,当时大量对c++11吹捧,特性还复杂,三位大神看不下去了,所以...,目标是设计网络时代和多核时代的C语言。

GO的另一种描述:"类C语言"、"21世纪C语言"。

特性起源:(1)并发思想:从CSP理论演化。->squeak->newsqueak(1989年)->alef(1993年)->go

(2)面向对象思想:algol->pascal->modula-2->oberon->-(object oberon)>oberon2->go

(3)c语言家族:Ken Thompson发明的B语言(1969年)->Dennis M.Ritchie发明的C语言(1972~1989年)逐步演化。

语言基础

数组、切片、字符串概述

        一般情况下数组和字符串数据结构使用最频繁,只有在不满足场景才会考虑链表、散列表(数组和链表的结合体)或更复杂的数据结构。这三个底层原始数据内存结构相同。       

        赋值和函数传参:都不会复制底层数据。字符串底层是字符数组,只读属性,无法修改底层数组元素,字符串只复制数据地址和长度;数组是整体复制;切片虽然底层也是对应数据类型的数组,但每个切片有独立的长度和容量信息,切片传参按切片头信息的部分(含有底层数据指针)按值传递方式。

        总之,Go的赋值和函数传参规则很简单,都是传值方式处理。但是闭包对外部变量是引用方式访问。

数组

1、固定长度、特定类型,长度是数组类型的一部分。只要长度或者元素类型不同,那就是不同的类型。所以Go很少直接使用数组(不同长度无法直接赋值)。切片可以动态增长和收缩,更灵活。

2、值语义,一个数组变量就表示整个数组。是一个完整的值。(eg:C中数组是隐式指向第一个元素指针),数组变量被赋值或传递,实际上复制整个数组,数组较大,会有较大开销,避免复制开销,可以传递数组指针。

3、通过数组指针访问数组元素写法和直接访问一样。数组是特殊的结构体,cap和len返回的结果相同。一般情况下for range迭代可以保证不会出现数组越界情况。

4、数组可以定义字符串数组、函数数组、结构体数组、接口数组、通道数组等等。

5、空数组(长度为0的数组)不占内存空间。可以用于通道同步操作,一般更倾向于无类型匿名结构代替空数组。

6、Go中,数组是字符串和切片的基础,对于数组的操作都可以用于字符串或切片中。

字符串

1、无法改变的字节序,只读字节数组,长度不是类型的一部分。Go源码要求utf8(utf8编码的unicode码点(rune)序列)(除了转义字符)。uft8的错误编码不会向后扩散是优秀特性之一。

2、for range不支持非utf8编码的string遍历。

3、底层结构

// reflect.StringHeader
type StringHeader struct {
    Data uintptr
    Len int
}

4、字符串实际是结构体:(1)字符串指向的底层字节数组;(2)字节长度。

复制string就是reflect.StringHeader的复制,不涉及底层数字复制。

5、不是切片,但是支持切片操作,不同位置切片底层访问是同一块内存(Heder信息相同)。一般字面常量对应同一个字符串常量。

6、[]rune实际是[]int32类型,rune是int32的别名,不是新类型。rune表示Unicode码点,用了21位。

切片(slice)

1、简而言之,slice是简化版动态数。因为动态数组长度不固定,长度不是slice的类型一部分。

2、结构体定义,和string类似,多了一个Cap成员表示切片指向内存最大容量。cap必须大于等于切片的长度。

type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}

3、slice可以和nil比较,slice底层数据指针为nil,slice才是nil,此时长度和容量无效。如果数据指针为nil,Len和Cap有值,说明slice本身已被破坏(eg:reflect.SliceHeader或者unsafe包对slice不正确修改)。

4、和数组相似,slice复制只是复制SliceHeader信息,而不会复制底层数据。

5、类型上:因为长度不是切片的类型部分,只有相同元素,才是相同的slice类型。

6、slice操作

(1)添加元素

1>使用内置泛型函数append。容量不足:会重新分配内存,导致内存分配和数据复制代价。容量充足:用append返回值更新切片本身。

2>往前追加或往后追加

sliceInt := []int{0,1,1}
appendInt := 6
newSlice := append(appendInt, sliceInt) // 往头追加, 一般会导致内存重新分配,所有元素重新分配,性能比往后追加方式差。
newSlice := append(sliceInt, appendInt) // 往后追加

3>指定位置的插入

func TestSlice(t *testing.T) {
	s := []int{1, 2, 3, 4, 5}
	s = append(s[:2], append([]int{8}, s[2:]...)...)
	fmt.Println(s)

	s = append(s[:2], append([]int{6, 6, 6}, s[2:]...)...)
	fmt.Println(s)
}

// 结果
[1 2 8 3 4 5]
[1 2 6 6 6 8 3 4 5]

3>指定位置插入,避免append调用会创建临时切片开销,使用copy函数,挪动指定长度位置,然后直接赋值。只能改变指定位置的元素。

func TestSlice(t *testing.T) {
	a := make([]int, 0, 5)
	a = append(a, 0, 1, 2)
	fmt.Println(a, len(a), cap(a))
	copy(a[1+1:], a[1:])
	fmt.Println(a, len(a), cap(a))
	a[1] = 6
	fmt.Println(a, len(a), cap(a))
}

输出结果
[0 1 2] 3 5
[0 1 1] 3 5
[0 6 1] 3 5

7、没有内置函数扩容切片容量,append扩展slice容量是副作用,本质是追加元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值