变量声明

golang变量以关键字var开头,变量名放中间,变量类型声明后置:

var 变量名 变量类型

1
2
3
var name string
var num int
var isok bool

为了简化掉var,还支持批量变量声明:

1
2
3
4
5
6
var(
a string
b int
c bool
d float32
)

变量赋值

和C++类似,变量声明会进行默认初始化,例如整型和浮点型默认为0,字符串类型默认为空字符串,布尔型默认为false,切片/函数/指针变量默认为nil。

同样可以在声明时通过表达式赋值:

1
2
var name string = "EdemMo"
var age int = 18

编译器还支持自动的类型推导,省略类型声明,甚至一个等式声明多种变量:

1
2
3
4
var name = "Eden"
var age = 18

var name,age = "Eden", 18 //ok

函数变量

全局变量使用上述方式进行声明,如果是函数内部的局部变量,无需var也无需类型,直接使用:=赋值也可以(不能在函数外使用):

1
2
3
4
5
6
7
8
9
package main
import "fmt"

var m = 100 //全局变量
func main() {
n := 10
m := 200 //使用局部变量
fmt.Println(m, n) //200 10
}
golang无需以分号结尾。

匿名变量

在多重赋值,使用下划线"_"代表匿名变量,既不占用命名空间,也不会为其分配内存,作占位符,表示忽略的值,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func foo() (int, string) {
return 18, "EdenMo"
}

func main() {
x, _ := foo()
_, y := foo()
fmt.Println("x=", x) //x= 18
fmt.Println("y=", y) //y= EdenMo
}

常量

同C一样,常量关键字为const,声明时必须赋值:

1
2
const pi = 3.1415
const e = 2.7182
也支持批量赋值,如果批量赋值省略后续值,代表和前一行值相同:
1
2
3
4
5
6
7
8
9
10
const (
a = 0.9
b = 0.8
c
d
)
func main() {
//a,b,c,d = 0.9 0.8 0.8 0.8
fmt.Println("a,b,c,d =", a, b, c, d)
}
### iota计数 iota专门用于在const表达式中进行自增计数,每新增一行声明,iota自动加一,如:
1
2
3
4
5
6
7
8
9
10
11
12
const (
a = iota // 0
b = iota // 1,
c // 2,等价于 c = iota
)

//新const块重新从0开始
const (
d = iota // 0
e // 1
f // 2
)
常用于定义枚举,go没有enum类型:
1
2
3
4
5
6
7
8
9
10
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)

也可以从1开始,也支持使用匿名变量跳过某些值(因为占位符不占据内存,因此是允许反复使用的):

1
2
3
4
5
6
7
8
9
10
type Weekday int
const (
Monday Weekday = iota+1 // 1
Tuesday // 2
Wednesday // 3
_ // 4
Friday // 5
_ // 6
Sunday // 7
)

注意,注意,iota是按行计数,而不是按次计数,同一行的iota值是相同的:

1
2
3
4
5
const (
a, b = iota, iota + 10 // a=0, b=10
c, d // c=1, d=11
e, f // e=2, f=12
)
注意此处每行常量数量必须和前一行相同,否则编译报错。

const赋值不影响iota自身的计数递增,因此可以混合赋值:

1
2
3
4
5
6
7
8
const (
a = iota // 0
b // 1
c = "hi" // "hi",iota 此时为 2
d // "hi",沿用上一行表达式,iota 为 3
e = iota // 4,重新使用 iota,值为当前行号
f // 5
)

也常用于表示存储递增:

1
2
3
4
5
6
7
8
const (
_ = iota
KB = 1 << (10 * iota)
MB = 1 << (10 * iota)
GB = 1 << (10 * iota)
TB = 1 << (10 * iota)
PB = 1 << (10 * iota)
)

数据类型

基本数据类型

  • 整型:uint8,uint16,uint32,uint64,int8,int16,int32,int64;

  • 特殊整型:uint,int;32位系统对应uint32/int32,64位则为uint64/int64;

  • uintptr:无符号整型,存放一个指针;

    同C/C++一样,golang支持进制赋值,额外支持下划线分隔符:

    1
    2
    3
    4
    var c int = 0xff
    fmt.Printf("%x \n", c) //格式化打印

    v := 123_456 //123456

  • 浮点型:float32、float64

  • 布尔:和C不同,golang的bool不允许和其他类型相互转换;

字符串string、字节byte和字符rune

  • Go约定使用UTF-8编码存储字符串,string一旦创建不能被修改;

  • byte: uint8类型,表示一个字节,用于处理二进制原始数据和ASCII字符;

  • rune:int32类型,表示一个字符,即一个Unicode码点;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main
import "fmt"

func main() {
s := "EdenMo"
fmt.Println(len(s)) //6

sw := "世界"
fmt.Print(len(sw), "\n") //6, 两个汉字6个字节

//按字节访问:
for i := 0; i < len(sw); i++ {
//byte 0 : 45byte 1 : 64byte 2 : 65byte 3 : 6ebyte 4 : 4dbyte 5 : 6f2
fmt.Printf("byte %d : %x", i, s[i]) //此处,s[i]是指第i个字节,而不是汉字
}

//按字符访问:
runeStr := []rune(sw)
fmt.Println(len(runeStr))
for i, r := range runeStr { //i遍历个数,r遍历字符
//%c转中文字符,%X转Unicode编码,每个汉字都对应一个
fmt.Printf("rune %d : %c (U + %X)\t", i, r, r) //rune 0 : 世 (U + 4E16) rune 1 : 界 (U + 754C)
}
}

反引号禁用转义

使用反引号定义的字符串自动忽略转义,被称为原生字符串字面量(Raw string literal),常用于写正则、SQL、多行文本等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
s := `Hello\t\nWorld`
fmt.Println(s) //Hello\t\nWorld

s_mult :=
`第一行
第二行
第三行`
fmt.Println(s_mult)
}
你会发现打印效果是:
1
2
3
第一行
第二行
第三行

这是因为反引号间我们的代码缩进tab也被打印出来了,要对齐,必须诚实地去除一切缩进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
s := `Hello\t\nWorld`
fmt.Println(s) //Hello\t\nWorld

s_mult :=
`第一行
第二行
第三行`
fmt.Println(s_mult)
}
效果:
1
2
3
第一行
第二行
第三行

字符串不可变与转化修改

同C类似,字面常量禁止修改字符:

1
2
s := "hello"
s[0] = 'H' //error!
如果必须转化,需经过byte或者rune处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
s := "Hello"
b := []byte(s)
b[0] = 'h'
s1 := string(b)
fmt.Println(s1) //hello

ss := "中国"
r := []rune(ss)
r[0] = '美'
ss1 := string(r)
fmt.Println(ss1) //美国
}

字符串操作

拼接、比较、切片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"fmt"
"strings"
)

func main() {
//简单拼接:
s := "Hello"
s = s + " World"
fmt.Println(s) //Hello World

//多次大量拼接建议使用strings.Builder
var bdstring strings.Builder
bdstring.WriteString("Hello")
bdstring.WriteString("World")
bdstring.WriteString("God")
s1 := bdstring.String()
fmt.Println(s1) //HelloWorldGod

//字符串比较
fmt.Println(s != s1) //true

//切片
s2 := s[0:5]
s3 := s1[0:5]
fmt.Println(s2 == s3) //true

//如果是Unicode切片,必须使用rune再切片
s4 := "你好"
r := []rune(s4)
s4Sub := string(r[1:2]) //2是下标,并非长度,左闭右开
fmt.Println(s4Sub)
}

其他辅助函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"strings"
)

func main() {
//是否包含子串
s := "EdenMo"
isContain := strings.Contains(s, "Edex")
fmt.Println(isContain) //false

//是否以子串开头
isPrefix := strings.HasPrefix(s, "Eden")
fmt.Println(isPrefix) //true

//是否以子串结尾
isSuffix := strings.HasSuffix(s, "Mo")
fmt.Println(isSuffix) //true

//查找字串第一次出现下标
id := strings.Index(s, "d")
fmt.Println(id) //1

//转换大小写:
s1 := strings.ToUpper(s)
fmt.Println(s1) //EDENMO
s2 := strings.ToLower(s)
fmt.Println(s2) //edenmo

//替换子串:
// 最后的数字n: n>0,替换n次;n=0,不替换;n<0,有多少替换多少
s3 := strings.Replace(s, "Eden", "Mike", -1)
fmt.Print(s3) //MikeMo

}
特别的,按分隔符分割:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
"strings"
)

func main() {
s := "a,b,c,d"
parts := strings.Split(s, ",")
for _, ss := range parts {
fmt.Println(ss) //a,b,c,d
}
//或使用Go风格默认打印
fmt.Printf("%#v\n", parts) //[]string{"a", "b", "c", "d"}
}

虽然字符串存储是按字节存储的,因此含中文或者其他多编码文本时切片时不能直接按序号截断,但golang支持按Unicode编码进行分割:

1
2
3
s := "GO世界和平"
parts := strings.Split(s, "")
fmt.Printf("%#v\n", parts) //[]string{"G", "O", "世", "界", "和", "平"}
此处使用空分隔符,字符串会自动按unicode编码分割,而不是不分割。

按字符串分隔符重新拼接:

1
2
3
parts := []string{"a", "b", "EdenMo"}
s := strings.Join(parts, ",")
fmt.Println(s) // "a,b,EdenMo"

复数

golang具有复数类型定义,分别是:

  • complex64: 实部和虚部都是32位;

  • complex128: 实部虚部均为64位;

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
var c1 complex64
c1 = 1 + 2i
var c2 complex64
c2 = 1 - 2i
c3 := c1 * c2
fmt.Println(c3) //(5+0i)
}

类型转换

golang只有强制类型转换:

T(表达式)

如:

1
2
3
var a, b = 3, 4
c := int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c) //5

运算符

大部分场景同C/C++,为了方便,直接类比和C/C++的差异。

自增、自检独立一行

golang没有前缀++或者--,后缀++/--只能作独立一行,不能作表达式值:

1
2
3
4
5
6
i := 10
i++
fmt.Println(i) //ok, 11

b := i-- +10 //error!
--i //error!

无三目运算符

只能使用条件赋值:

1
2
3
4
5
6
7
var x int
if true{
x = a
}
else{
x = b
}

不支持指针算术

在C/C++中:

1
2
int *p = arr;
p++; //ok
而golang中:
1
2
var p *int
p++ //error!

golang位移必须使用无符号整型或者非负常量

1
2
3
4
5
6
7
8
9
10
var b int = 1
const a = 10
var a1 = 10
fmt.Println(b << a) //ok
fmt.Println(b << a1) //ok

var a2 int = 10
var a3 int = -1
fmt.Println(b << a2) //带符号整数,部分编译器允许,但不符合严格规范
fmt.Println(b << a3) //带符号负数,必然抱错

golang数据类型比较规则

在golang中,对operator==和operator!=:

  • golang不支持自定义重载运算符;

  • 基本数据类型均能够直接比较;

  • slice、map、func不能互相比较,只能判断是否为nil(和nil比较);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package main

    import "fmt"

    func f1() int {
    return 0
    }

    //无类型声明,即void
    func f2() {}

    func main() {
    //slice只能与nil比较
    var s1 []int
    s2 := []int{1, 2, 3}
    var s3 []int = nil

    fmt.Println(s1 == nil && s3 == nil && s2 == nil)

    //error!
    //fmt.Println(s1 == s2)
    //fmt.Println(s2 == s2)

    //map也只能与nil比较:
    m1 := map[string]int{"a": 1}

    var m2 map[int]int //声明和定义分离
    m2 = map[int]int{1: 2, 3: 4}
    m2[3] = 5 //直接替代4

    //make声明
    m3 := make(map[int]int)
    m3[0] = 1

    fmt.Println(m1 == nil || m2 == nil || m3 == nil)

    //func亦是只能与nil比较
    fn1 := f1
    var fn2 func()
    var fn3 func() = nil
    fmt.Println(fn1 == nil) //false
    fmt.Println(fn2 == nil && fn3 == nil) //true
    }

  • array、struct中元素均可比较,那么也能直接比较,否则不能比较;

    对struct而言,以下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package main

    import "fmt"

    type Person struct {
    Name string
    Age int
    }

    type User struct {
    Name string
    Tags []string //slice类型不可比较
    }

    func main() {
    p1 := Person{"Eden", 18}
    p2 := Person{"Mike", 18}
    fmt.Println(p1 == p2) //ok, false

    u1 := User{"A", []string{"Eden", "Mo"}}
    u2 := User{"A", []string{"Eden", "Mo"}}
    fmt.Println(u1 == u2) //error!不可比较!
    }

    对array,能够嵌套讨论,含不能比较struct不能比较:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import "fmt"

    type Person struct {
    Name string
    Age int
    }

    type User struct {
    Name string
    Tags []string //slice类型不可比较
    }

    func main() {
    a1 := [3]int{1, 2, 3}
    a2 := [3]int{1, 2, 3}
    fmt.Println(a1 == a2) // ok, int array能够直接比较

    a3 := [2]Person{{"Eden", 18}, {"Mo", 18}}
    a4 := [2]Person{{"Eden", 18}, {"Mo", 18}}
    fmt.Println(a3 == a4) //ok, Person array是合法的

    a5 := [2]User{{"Eden", []string{"Eden", "Mo"}}, {"Mike", []string{"Mike", "Wong"}}}
    a6 := [2]User{{"Eden", []string{"Eden", "Mo"}}, {"Mike", []string{"Mike", "Wong"}}}
    fmt.Println(a5 == a6) //error! User struct array不能比较!

    }

数组Array

golang的数组有以下鲜明的特点:

  1. 数组长度固定且必须为常量,一旦定义不可修改;

  2. 长度和元素类型同时构成一个数组的数据类型,var a[5] int和var a[10]int是不同的数据类型;

  3. 数组是值类型,直接赋值和传参会复制整个数组和创建副本,传递的是值而并非指针,而不是修改原来的值;

一维数组初始化

有若干种初始化方法,既可以自动推导长度,还可以按索引分配元素,其余元素保留默认值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var a1 = [5]int{1, 2, 3, 4, 5}
var a2 = [...]int{1, 2, 3, 4, 5} //可以自动推导长度
var a3 [5]int = [5]int{1, 2, 3, 4, 5}
var str = [5]string{3: "third", 4: "fourth"}

func main() {
la1 := [3]int{1, 2, 3}
la2 := [...]int{1, 2}
la3 := [...]string{3: "third", 4: "fourth"} //自动推导+指定序号,实际分配等效于4+1,即5
la4 := [...]struct {
name string
age int
}{
{"Eden", 18},
{"Mike", 18}, //就算后续没元素,这里也要加逗号
}
}

最常用数字遍历和range遍历方法,是等效的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
la4 := [...]struct {
name string
age int
}{
{"Eden", 18},
{"Mike", 18}, //就算后续没元素,这里也要加逗号
}
for i, elem := range la4 {
fmt.Println(i, elem)
}

for i := 0; i < len(la4); i++ {
fmt.Println(i, la4[i])
}

多维数组

多维数组只有最外层能够使用...进行数组长度推导,其他维度不支持推导:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "fmt"

func main() {
a1 := [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
a2 := [...][2]int{
{1, 2},
{3, 4},
}

//二维遍历:
//0 0 1
//0 1 2
//0 2 3
//1 0 4
//1 1 5
//1 2 6
for i, v1 := range a1 {
for j, v2 := range v1 {
fmt.Println(i, j, v2)
}
}
fmt.Println("\n")

// 0 0 1
// 0 1 2
// 1 0 3
// 1 1 4
for i := 0; i < len(a2); i++ {
for j := 0; j < len(a2[i]); j++ {
fmt.Println(i, j, a2[i][j])
}
}
}

值传递、指针传递和切片传递

golang没有引用传递,要避免值传递的副本内存和复制开销,可以使用指针传递或者切片传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"runtime"
)

// 实现函数名打印函数
func getRunTimeInfo() (pc uintptr, file string, line int, ok bool) {
return runtime.Caller(1) //1代表调用者,0代表当前函数,2代表调用者的调用者,以此类推
}

// 值传递
func test(x [2]int) {
pc, file, line, _ := getRunTimeInfo()
fmt.Printf("[%s][line_%d][%s]: %p\n", file, line, runtime.FuncForPC(pc).Name(), &x)
}

// 指针传递
func testPtr(x *[2]int) {
pc, file, line, _ := getRunTimeInfo()
fmt.Printf("[%s][line_%d][%s]: %p\n", file, line, runtime.FuncForPC(pc).Name(), x)
}

// 切片传入的实际也是指针
func testSlice(x []int) {
pc, file, line, _ := getRunTimeInfo()
fmt.Printf("[%s][line_%d][%s]: %p\n", file, line, runtime.FuncForPC(pc).Name(), x) //切片拿到的
}

func main() {
a := [2]int{1, 2}
fmt.Printf("raw pointer: %p\n", &a)
test(a)
testPtr(&a)
testSlice(a[:])
}

打印效果:

1
2
3
4
raw pointer: 0x112e998e60b0
[d:/Documents/Desktop/alpha_go/main.go][line_14][main.test]: 0x112e998e60e0
[d:/Documents/Desktop/alpha_go/main.go][line_19][main.testPtr]: 0x112e998e60b0
[d:/Documents/Desktop/alpha_go/main.go][line_24][main.testSlice]: 0x112e998e60b0

区分指针数组和数组指针

数组指针:*[N]T,星号在括号前

指针数组:[N]*T,星号在括号后

数组指针:

1
2
3
4
5
6
a := [3]int{1, 2, 3}
var p *[3]int = &a
a[2] = 5

fmt.Println((*p)[2]) //5
p[2] = 6

指针数组:

1
2
3
4
5
6
7
8
9
a, b, c := 1, 2, 3
var arr [3]*int = [...]*int{&a, &b, &c}
arr1 := [...]*int{&a, &b}

*arr[0] = 3
*arr[2] = 5
*arr1[1] = 4

println(a, b, c) //3 4 5

数组array的长度是固定的,因此增删、排序对其操作实际很不方便,常用的做法是转换到切片的视角:

1
2
3
4
a := [5]int{1,2,3,4,5}
s := a[:] //s和a指向同一个地址

s.append(s,6) //方便调用切片的各种算法和接口
转换到切片后,如果append产生了动态增长空间,那么会重新分配地址空间(此时切片和数组的关联就完全断开了),如果还有必要转回数组,可以:
1
2
3
4
5
6
7
8
9
10
11
12
13
a := [...]int{1, 2, 3, 4, 5}
s := a[:]

s = append(s, 7)
//产生动态扩容后,s和a不再指向同一块地址
s[3] = 10
//虽然扩容到6,但是仍然可以指定更长的空间,因为copy是值传递
var b [10]int

copy(b[:], s)

fmt.Printf("%#v ", a) //[5]int{1, 2, 3, 4, 5}, 可见s修改已经不再影响a了
fmt.Printf("%#v ", b) //[10]int{1, 2, 3, 10, 5, 7, 0, 0, 0, 0}

切片Slice

golang没有引用,引入切片方便了动态存储和扩容,特点如下:

  • 切片是对底层数组的一个视图,本质是一种描述结构:

    1
    2
    3
    4
    5
    type slice struct{
    array unsafe.Pointer //底层数组指针
    len int //长度,切片起始至末尾
    cap int //容量,切片起始至底层数组末尾
    }

  • 一个底层数组可以有多个切片视图,从数组到切片是指针传递,类似引用;而从切片到切片相当于结构体拷贝,为值传递;

  • 长度可变,属于一种可变数组,空切片的len和cap都是0;

切片的初始化

需要区分nil切片和空切片,空切片含有合法的切片头,但len和cap都是0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//nil切片
var s1 []int
fmt.Println(s1 == nil, len(s1), cap(s1)) //true 0 0

//空切片
s2 := []int{}
fmt.Println(s2 == nil, len(s2), cap(s2)) //false 0 0

//这也是空切片,分配0个长度,1个容量
var s3 []int = make([]int, 0, 1)
fmt.Println(s3 == nil, len(s3), cap(s3)) //false 0 1

//len=3, cap=4
var s4 []int = make([]int, 3, 4)
fmt.Println(s4 == nil, len(s4), cap(s4)) //false 3 4
可见也可以直接指定长度和容量,创建切片时自动分配底层数组;

区分cap和len

主要是从数组切片而来,cap和len可能是不一样的,起点都是切片的起点,但cap的计数终点是数组的末尾,而len是切片的末尾,如:

1
2
3
4
var a = [...]int{0, 1, 2, 3, 4, 5, 6}
s := a[2:4]
fmt.Println(len(s)) //2,仅包含下标为2、3元素
fmt.Println(cap(s)) //5,结尾为6,2-6共5个元素
当对切片操作使得len==cap时,会重新分配内存,对于小切片,会按2倍策略增长;对大切片而言会采取平缓的增长策略,不同编译器版本有细节差异;

区分golang内存分配make和new

golang中也提供new操作,用于开辟新的内存,这个内存可能在栈上,也可能逃逸在堆上,但无需用户手动delete,栈上的数据会在生命周期结束被系统快速回收,在堆上的数据会被go的gc自动管理和回收;

对slice、array、map以及channel类型,golang提供了make初始化方法,对比new方法,除了分配内存,还会进行空间初始化操作,所以一般在需要基本数据类型指针的场合,使用new,而对于这四种类型,使用make,如果强行使用new,很可能遇到panic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
)
func main() {
//int
v := new(int)
*v = 81
fmt.Println(*v) //ok,81

//array
arr := new([5]int)
(*arr)[0] = 81
fmt.Println(arr) //ok, &[81 0 0 0 0]

//slice
sli := new([]int)
(*sli)[0] = 81
fmt.Println(sli) //error: panic!

//map
mp := new(map[int]int)
(*mp)[0] = 81
fmt.Println(mp) //error: panic!
}

这是因为slice、map、channel都算类似引用的类型,golang给引用类型赋nil,直接给nil赋值会导致panic,因此必须使用make,除了开辟内存,还会将其初始化为0值而非nil:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
)

func main() {
//int
v := new(int)
*v = 81
fmt.Println(*v) //ok,81

//array
arr := make([]int, 5)
arr[0] = 81
fmt.Println(arr) //ok, [81 0 0 0 0]

//slice
sli := make([]int, 5, 5)
sli[0] = 81
fmt.Println(sli) //ok,[81 0 0 0 0]

//map
mp := make(map[int]int)
mp[0] = 81
fmt.Println(mp) //ok:map[0:81]
}
值得注意的细节是,new返回的是指针,需要解指针再赋值,而make返回的就是对象本身类型;

参考链接:

  1. Go语言基础之变量和常量

  2. 切片Slice

  3. golang new和make的区别