GO语言学习笔记(一)

一、变量

1.1 标准格式

var age int

1.2 基本类型

- bool

- string

- int(随系统,一般是占用4个字节)、int8(占一个字节)、int16(占两个字节)、int32(占4个字节)、int64(占8个字节)

- uint(无符号整数)、uint8、uint16、uint32、uint64、uintptr

- byte // `uint8 的别名`

- rune // `int32 的别名 代表一个 Unicode 码`

- float32、float64

- complex64、complex128

当一个变量被声明之后,系统自动赋予它该类型的零值:

`int 为 0`,`float 为 0.0`,`bool 为 false`,`string 为空字符串`,`指针为 nil `

1.3 不指明变量类型

例如:

var level = 1

这种声明变量的方式,直接赋值了,并没有指明类型,Go语言中,在编译时会自动推导类型,可以使用  fmt.Print("%T",level)  进行类型输出。

1.4 批量格式

var (

a int

b string

c []float32

)

1.5 简短格式

i := 1

使用简短格式有以下限制:1. 定义变量,同时显式初始化;2. 不能提供数据类型;3. 只能用在函数内部

1.6 匿名变量

 匿名变量以“_”下划线表示;;;匿名变量不占用命名空间,也不会分配内存。匿名变量可以重复声明使用;;;“_”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。

1.7 作用域

1.局部变量

在函数体内声明的变量称之为`局部变量`,它们的作用域`只在函数体内`,函数的参数和返回值变量都属于局部变量。。。。局部变量不是一直存在的,它只在定义它的函数被调用后存在,函数调用结束后这个局部变量就会被销毁。

package main
import (
    "fmt"
)
func main() {
    //声明局部变量 a 和 b 并赋值
    var a int = 3
    var b int = 4
    //声明局部变量 c 并计算 a 和 b 的和
    c := a + b
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

2.全局变量

在函数体外声明的变量称之为`全局变量`,全局变量只需要在`一个源文件中定义,就可以在所有源文件中使用`,当然,不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量。*******全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的`首字母必须大写`。

Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。

package main
import "fmt"
//声明全局变量
var bb float32 = 3.14
func main() {
	bb := 3
	fmt.Println(bb)
}
//执行结果 3

3.形式参数

在定义函数时函数名后面括号中的变量叫做`形式参数`(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。形式参数会作为`函数的局部变量来使用`。

package main
import (
    "fmt"
)
//全局变量 a
var a int = 13
func main() {
    //局部变量 a 和 b
    var a int = 3
    var b int = 4
    fmt.Printf("main() 函数中 a = %d\n", a)
    fmt.Printf("main() 函数中 b = %d\n", b)
    c := sum(a, b)
    fmt.Printf("main() 函数中 c = %d\n", c)
}
func sum(a, b int) int {
    fmt.Printf("sum() 函数中 a = %d\n", a)
    fmt.Printf("sum() 函数中 b = %d\n", b)
    num := a + b
    return num
}

二、整型

2.1 整型

Go语言同时提供了有符号和无符号的整数类型。

* 有符号整型:int、int8、int64、int32、int64

* 无符号整型:uint、uint8、uint64、uint32、uint64、uintptr

2.2 浮点型

1. `float32` : 范围 约1.4e-45 到 约3.4e38

2. `float64` :范围约4.9e-324 到 约1.8e308

floatStr1 := 3.2
//保留小数点位数
fmt.Printf("%.2f\n", floatStr1)

通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。

2.3 布尔型

在Go语言中,以bool类型进行声明:

var aaa bool
\\`==`,`>`,`<`,`<=`, `>=`,`&&(AND)`,`||(OR)`等都会产生bool值
var aVar = 10
aVar == 5  // false
aVar == 10 // true
aVar != 5  // true
aVar != 10 // false

go语言中,只有相同类型的值才能比较,如果是接口(interface),也都必须实现了相同的接口。如果其中一个值是-常量,那另一个可以不是常量,但是类型要和常量类型相同。如果都不满足,必须转换同类型。

‘&&(AND)`,`||(OR)`是具有短路行为的,如果运算符左边的值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值。(&&优先级高于||)

var a = 10
	//因为a>11已经不满足了,所以a < 30不会走,整个表达式为false
	if(a > 11 && a < 30){
		fmt.Println("正确")
	}else{
		fmt.Println("错误")
	}

	//因为a > 5已经满足了,所以a < 30不会走,整个表达式为true
	if(a > 5 || a < 30){
		fmt.Println("正确")
	}else{
		fmt.Println("错误")
	}

2.4 字符类型

GO的字符有两种:

- 一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。

- 另一种是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。

**ASCII 码的一个字符占一个字节**

**ASCII** 定义 128 个字符,由码位 0 – 127 标识。它涵盖英文字母,拉丁数字和其他一些字符。

**Unicode** 是 ASCII 的超集,它定义了 1,114,112 个代码点的代码空间。 Unicode 版本 10.0 涵盖 139 个现代和历史文本集(包括符文字母,但不包括 Klingon )以及多个符号

2.5 字符串

一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是 UTF-8 字符的一个序列。

var mystr string = "hello"
*字符串中可以使用转义字符来实现换行、缩进等效果,常用的转义字符包括:**
1. `\n:`换行符
2. `\r:`回车符
3. `\t:`tab 键
4. `\u 或 \U:`Unicode 字符
5. \\:反斜杠自身
对于字符长度,ASCII字符使用`len()`函数
Unicode字符串长度使用`utf8.RuneCountInString()`函数

字符串拼接符“+”或者`WriteString()`

s := "hel" + "lo,"
s += "world!"
fmt.Println(s) //输出 “hello, world!”

str1 := "什么,"
str2 := "东西"
var stringBuilder bytes.Buffer
//节省内存分配,提高处理效率
stringBuilder.WriteString(str1)
stringBuilder.WriteString(str2)
fmt.Println(stringBuilder.String())//输出“什么东西”

字符串索引:直接索引对rune类型无效,使用string方法转换

//使用:string([]rune(str6)[0])

var myStr01 string = "hello,深圳大学"
fmt.Println(string([]rune(myStr01)[6])) //显示“深”

字符串遍历:unicode字符集使用`for range`进行遍历,ascii字符集可以使用`for range`或者`for`循环遍历

var str1 string = "hello"
	var str2 string = "hello,深圳大学"
	// 遍历
	for i :=0; i< len(str1); i++{
		fmt.Printf("ascii: %c %d\n", str1[i], str1[i])
	}
	for _, s := range  str1{
		fmt.Printf("unicode: %c %d\n ", s, s)
	}
	// 中文只能用 for range
	for _, s := range  str2{
		fmt.Printf("unicode: %c %d\n ", s, s)
	}

字符串格式化

%c  单一字符
%T  动态类型
%v  本来值的输出
%+v 字段名+值打印
%d  十进制打印数字
%p  指针,十六进制
%f  浮点数
%b 二进制
%s string

字符串查找

//strings.Index(): 正向搜索子字符串
//strings.LastIndex():反向搜索子字符串

2.6类型转换

//类型 B 的值 = 类型 B(类型 A 的值)
valueOfTypeB = type B(valueOfTypeA)

fmt.Println("int8 range:", math.MinInt8, math.MaxInt8)
fmt.Println("int16 range:", math.MinInt16, math.MaxInt16)
fmt.Println("int32 range:", math.MinInt32, math.MaxInt32)
fmt.Println("int64 range:", math.MinInt64, math.MaxInt64)
// 初始化一个32位整型值
var a int32 = 1047483647
// 输出变量的十六进制形式和十进制值
fmt.Printf("int32: 0x%x %d\n", a, a)
// 将a变量数值转换为十六进制, 发生数值截断
b := int16(a)
// 输出变量的十六进制形式和十进制值
 fmt.Printf("int16: 0x%x %d\n", b, b)
// 将常量保存为float32类型
var c float32 = math.Pi
// 转换为int类型, 浮点发生精度丢失
fmt.Println(int(c))

2.7字符串修改

修改字符串时,可以将字符串`转换为[]byte`进行修改

**[]byte和string可以通过强制类型转换**

s1 := "localhost:8080"
fmt.Println(s1)
// 强制类型转换 string to byte
strByte := []byte(s1)

// 下标修改
strByte[len(s1)-1] = '1'
fmt.Println(strByte)

// 强制类型转换 []byte to string
s2 := string(strByte)
fmt.Println(s2)

三、指针

1常量

go语言中,常量使用关键字‘const’定义,用于 存储不会改变的数据,一般是`布尔型`、`数字型`(整数型、浮点型和复数)和`字符串型`。

声明格式:
const name[type] = value
const pi = 3.1415926 //type可以省略。且和变量声明一样,可以批量声明多个。
//如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式,对应的常量类型也是一样的。

常量声明也可以用iota,用于生成一组以相似规则初始化的常量,例如:

const (
    Sunday  = iota //0
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday  //6
)//在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加1

2.指针

可以分为两类,一个是类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算;另一个是切片,由指向起始元素的原始指针、元素数量和容量组成。

Go语言中使用在变量名前面添加`&`操作符(前缀)来获取变量的内存地址(取地址操作)

创建指针的另一种方法:  new(类型)

str := new(string)
*str = "hahahahah"
fmt.Prinln(*str)//new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

3.堆栈

3.1. 堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态加入到堆上(堆被扩张)。当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);

3.2. 栈(stack):栈又称堆栈, 用来存放程序暂时创建的局部变量,也就是我们函数的大括号`{ }`中定义的局部变量。

4.类型别名

格式:

type TypeAlias = Type//TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类型

type Name Type       //定义Name为Type类型 ,定义之后 Name为一种新的类型

对于类型别名和类型的区别:

package main
import (
    "fmt"
)
// 将NewInt定义为int类型
type NewInt int
// 将int取一个别名叫IntAlias
type IntAlias = int
func main() {
    // 将a声明为NewInt类型
    var a NewInt
    // 查看a的类型名 main.NewInt
    fmt.Printf("a type: %T\n", a)
    // 将a2声明为IntAlias类型
    var a2 IntAlias
    // 查看a2的类型名 int 
    //IntAlias 类型只会在代码中存在,编译完成时,不会有 IntAlias 类型。
    fmt.Printf("a2 type: %T\n", a2)
}

5.字符串与其他数据类型的转换

5.1整数 与 字符串

   // 字符串与其他类型的转换
   // str 转 int
   newStr1 := "1"
   intValue, _ := strconv.Atoi(newStr1)
   fmt.Printf("%T,%d\n", intValue, intValue)  // int,1
   
   // int 转 str
   intValue2 := 1
   strValue := strconv.Itoa(intValue2)
   fmt.Printf("%T, %s\n", strValue, strValue)

2. 浮点数 与字符串

      // str 转  float
       string3 := "3.1415926"
       f,_ := strconv.ParseFloat(string3, 32)
       fmt.Printf("%T, %f\n", f, f)  // float64, 3.141593
       //float 转 string
   	floatValue := 3.1415926
   	//4个参数,1:要转换的浮点数 2. 格式标记(b、e、E、f、g、G)
   	//3. 精度 4. 指定浮点类型(32:float32、64:float64)
   	// 格式标记:
   	// ‘b’ (-ddddp±ddd,二进制指数)
   	// ‘e’ (-d.dddde±dd,十进制指数)
   	// ‘E’ (-d.ddddE±dd,十进制指数)
   	// ‘f’ (-ddd.dddd,没有指数)
   	// ‘g’ (‘e’:大指数,‘f’:其它情况)
   	// ‘G’ (‘E’:大指数,‘f’:其它情况)
   	//
   	// 如果格式标记为 ‘e’,‘E’和’f’,则 prec 表示小数点后的数字位数
   	// 如果格式标记为 ‘g’,‘G’,则 prec 表示总的数字位数(整数部分+小数部分)
   	formatFloat := strconv.FormatFloat(floatValue, 'f', 2, 64)
   	fmt.Printf("%T,%s",formatFloat,formatFloat)

四.数组

1.数组

数组的长度固定,因此在GO中很少直接使用。数组的声明:

var 数组变量名 [元素数量]Type
//数组变量名:数组声明及使用时的变量名。
//元素数量:数组的元素数量,可以是一个表达式,但最终通过编译期计算的结果必须是整型数值,元素数量不能含有到运行时才能确认大小的数值。
//Type:可以是任意基本类型,包括数组本身,类型为数组本身时,可以实现多维数组。

数组取值:

//1. 通过索引下标取值,索引从0开始
   	fmt.Println(arr[0])
   	fmt.Println(arr[1])
   	fmt.Println(arr[2])

//2. for range获取
   for index,value := range arr{
   		fmt.Printf("索引:%d,值:%d \n",index,value)
   }

数组赋值:

1. 初始化的时候赋值

   var arr [3]int = [3]int{1,2,3}
   //如果第三个不赋值,就是默认值0
   var arr [3]int = [3]int{1,2}
   //可以使用简短声明
   arr := [3]int{1,2,3}
   //如果不写数据数量,而使用...,表示数组的长度是根据初始化值的个数来计算
   arr := [...]int{1,2,3}

2. 通过索引下标赋值

   	var arr [3]int
   	arr[0] = 5
   	arr[1] = 6
   	arr[2] = 7

> 一定要注意,数组是定长的,不可更改,在编译阶段就决定了

`小技巧:` 如果觉的每次写 `[3]int` 有点麻烦,你可以为 `[3]int` 定义一个新的类型。

	type arr3 [3]int
	//这样每次用arr3 代替[3]int,注意前面学过 定义一个类型后 arr3就是一个新的类型
	var arr arr3
	arr[0] = 2
	for index,value := range arr{
		fmt.Printf("索引:%d,值:%d \n",index,value)
	}

**如果想要只初始化第三个值怎么写?**

	//2 给索引为2的赋值 ,所以结果是 0,0,3
	arr := [3]int{2:3}
	for index,value := range arr{
		fmt.Printf("索引:%d,值:%d \n",index,value)
	}

2.多维数组

var array_name [size1][size2]...[sizen] array_type
//array_name 为数组的名字,array_type 为数组的类型,size1、size2 等等为数组每一维度的长度。

// 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化数组中指定的元素
array = [4][2]int{1: {0: 20}, 3: {1: 41}}

3.切片

从连续内存区域生成切片是常见的操作,格式如下:

slice [开始位置 : 结束位置]

语法说明如下:

- slice:表示目标切片对象;
- 开始位置:对应目标切片对象的索引;
- 结束位置:对应目标切片的结束索引。

从数组生成切片,代码如下:

var a  = [3]int{1, 2, 3}
//a[1:2] 生成了一个新的切片
fmt.Println(a, a[1:2])

从数组或切片生成新的切片拥有如下特性:

- 取出的元素数量为:结束位置 - 开始位置;

- 取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取;

- 当缺省开始位置时,表示从连续区域开头到结束位置`(a[:2])`;

- 当缺省结束位置时,表示从开始位置到整个连续区域末尾`(a[0:])`;

- 两者同时缺省时,与切片本身等效`(a[:])`;

- 两者同时为 0 时,等效于空切片,一般用于切片复位`(a[0:0])`。

3.1声明新切片

var name []Type

// 声明字符串切片
var strList []string
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{}
// 输出3个切片
fmt.Println(strList, numList, numListEmpty)
// 输出3个切片大小
fmt.Println(len(strList), len(numList), len(numListEmpty))
// 切片判定空的结果
fmt.Println(strList == nil)    //true
fmt.Println(numList == nil)    //true
fmt.Println(numListEmpty == nil)    //true

切片是动态结构,只能与 nil 判定相等,不能互相判定相等。声明新的切片后,可以使用 append() 函数向切片中添加元素。

var strList []string
// 追加一个元素
strList = append(strList,"shenmedongxi")
fmt.Println(strList)

3.2make()构造切片

如果需要动态地创建一个切片,可以使用 make() 内建函数,格式如下:

make( []Type, size, cap )

`Type` 是指切片的元素类型,`size` 指的是为这个类型分配多少个元素,`cap `为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,`降低多次分配空间造成的性能问题`

a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
//容量不会影响当前的元素个数,因此 a 和 b 取 len 都是 2
//但如果我们给a 追加一个 a的长度就会变为3
fmt.Println(len(a), len(b))

使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。因此:

	var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	myslice := numbers4[4:6]
	//这打印出来长度为2
	fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice))
	myslice = myslice[:cap(myslice)]
	//为什么 myslice 的长度为2,却能访问到第四个元素
	fmt.Printf("myslice的第四个元素为: %d", myslice[3])

3.3切片复制

Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。

copy() 函数的使用格式如下:

copy( destSlice, srcSlice []T) int
//其中 `srcSlice `为数据来源切片,`destSlice `为复制的目标(也就是将 srcSlice 复制到destSlice),`目标切片必须分配过空间且足够承载复制的元素个数`,并且来源和目标的`类型必须一致`,copy() 函数的返回值表示实际发生复制的元素个数。

下面的代码展示了使用 copy() 函数将一个切片复制到另一个切片的过程:

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置

4.map

map 是一种无序的`键值对`的集合。map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,map 是无序的,我们无法决定它的返回顺序,这是因为 map 是使用 hash 表来实现的。

map 是引用类型,可以使用如下方式声明:

//[keytype] 和 valuetype 之间允许有空格。
var mapname map[keytype]valuetype
- mapname 为 map 的变量名。
- keytype 为键类型。
- valuetype 是键对应的值类型。

在声明的时候不需要知道 map 的长度,因为 map 是可以动态增长的,未初始化的 map 的值是 nil,使用函数 len() 可以获取 map 中 键值对的数目。

package main
import "fmt"
func main() {
    var mapLit map[string]int
    var mapAssigned map[string]int
    mapLit = map[string]int{"one": 1, "two": 2}
    mapAssigned = mapLit
    //mapAssigned 是 mapList 的引用,对 mapAssigned 的修改也会影响到 mapList 的值。
    mapAssigned["two"] = 3
    fmt.Printf("Map literal at \"one\" is: %d\n", mapLit["one"])
    fmt.Printf("Map assigned at \"two\" is: %d\n", mapLit["two"])
    fmt.Printf("Map literal at \"ten\" is: %d\n", mapLit["ten"])
}

map的另外一种创建方式:

make(map[keytype]valuetype)

map2 := make(map[string]int, 100)

既然一个 key 只能对应一个 value,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办?**

答案是:使用`切片`

例如,当我们要处理 unix 机器上的所有进程,以父进程(pid 为整形)作为 key,所有的子进程(以所有子进程的 pid 组成的切片)作为 value。

通过将 value 定义为 []int 类型或者其他类型的切片,就可以优雅的解决这个问题,示例代码如下所示:

mp1 := make(map[int][]int)
mp2 := make(map[int]*[]int)

4.1遍历map

map 的遍历过程使用 for range 循环完成,代码如下:

scene := make(map[string]int)
scene["cat"] = 66
scene["dog"] = 4
scene["pig"] = 960
for k, v := range scene {
    fmt.Println(k, v)
}

注意:map是无序的,不要期望 map 在遍历时返回某种期望顺序的结果

4.2删除

使用 delete() 内建函数从 map 中删除一组键值对,delete() 函数的格式如下:

delete(map, 键)//map 为要删除的 map 实例,键为要删除的 map 中键值对的键。
scene := make(map[string]int)
// 准备map数据
scene["cat"] = 66
scene["dog"] = 4
scene["pig"] = 960
delete(scene, "dog")
for k, v := range scene {
    fmt.Println(k, v)
}

注意map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。

4.3线程安全的map

需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 `sync.Map`,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。

sync.Map 有以下特性:

- 无须初始化,直接声明即可。

- sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。

- 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。

package main
import (
      "fmt"
      "sync"
)
func main() {
    //sync.Map 不能使用 make 创建
    var scene sync.Map
    // 将键值对保存到sync.Map
    //sync.Map 将键和值以 interface{} 类型进行保存。
    scene.Store("greece", 97)
    scene.Store("london", 100)
    scene.Store("egypt", 200)
    // 从sync.Map中根据键取值
    fmt.Println(scene.Load("london"))
    // 根据键删除对应的键值对
    scene.Delete("london")
    // 遍历所有sync.Map中的键值对
    //遍历需要提供一个匿名函数,参数为 k、v,类型为 interface{},每次 Range() 在遍历一个元素时,都会调用这个匿名函数把结果返回。
    scene.Range(func(k, v interface{}) bool {
        fmt.Println("iterate:", k, v)
        return true
    })
}

5.nil

在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串`" "`,而指针、切片、映射、通道、函数和接口的零值则是 nil。

nil 标识符是不能比较的

package main
import (
    "fmt"
)
func main() {
    //invalid operation: nil == nil (operator == not defined on nil)
    fmt.Println(nil==nil)
}//错误

6.make 和 new

make 关键字的主要作用是创建 slice、map 和 Channel 等内置的数据结构,而 new 的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。

1. make 分配空间后,会进行初始化,new分配的空间被清零

2. new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type;

3. new 可以分配任意类型的数据;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值