学习Go语言基础类语法_Day01

目录

1、变量

1.1、变量的声明

1.1.1、语法

var identifier type

1.1.2、批量声明

var (
		name string
		age  int
		ok   bool
	)

1.2、变量的初始化

1.2.1、语法

var 变量名 类型 = 表达式
var name string = "Caidd123"
var site string = "www.Caidd123.com"
var age int = 23

1.2.2、类型推导

var name  = "Caidd123"
var age   = 23

可以根据初始化值进行类型推导,从而省略类型。

1.2.3、同时初始化多个变量

var name, site, age = "Caidd123", "www.Caidd123.com", 20

1.2.4、*短变量声明(最常用)

name := "Caidd123"
age := 23

注意:只适合在函数内部,函数外面不能使用。

1.2.5、匿名变量(_)

接收到多个变量,有一些变量使用不到时,可以使用下划线_表示变量名称

name, _ := func()

2、常量

2.1、语法

const constantName [type]= value

2.2、实例

package main

func main() {
	const PI float64 = 3.14
	const PI2 = 3.1415 // 可以省略类型

	const (
		width  = 100
		height = 200
	)

	const i, j = 1, 2 // 多重赋值
	const a, b, c = 1, 2, "foo"
}

const同时声明多个常量时,如果省略了值则表示和上面一行的值相同

package main

import "fmt"

func main() {
	const (
		a1 = 100
		a2
		a3
	)

	fmt.Printf("a1: %v\n", a1)
	fmt.Printf("a2: %v\n", a2)
	fmt.Printf("a3: %v\n", a3)
}

运行结果

a1: 100
a2: 100
a3: 100

2.3、iota

iota 比较特殊,可以被认为是一个可被编译器修改的常量,它默认开始值是0,每调用一次加1。遇到 const 关键字时被重置为 0

2.3.1、iota实例

package main

import "fmt"

func main() {
	const (
		a1 = iota
		a2 = iota
		a3 = iota
	)

	fmt.Printf("a1: %v\n", a1)
	fmt.Printf("a2: %v\n", a2)
	fmt.Printf("a3: %v\n", a3)
}

运行结果

a1: 0
a2: 1
a3: 2

2.3.2、使用_跳过某些值

package main

import "fmt"

func main() {
	const (
		a1 = iota
		_
		a2 = iota
	)

	fmt.Printf("a1: %v\n", a1)
	fmt.Printf("a2: %v\n", a2)
}

运行结果

a1: 0
a2: 2

2.3.3、iota声明中间插队

package main

import "fmt"

func main() {
	const (
		a1 = iota
		a2 = 100
		a3 = iota
	)

	fmt.Printf("a1: %v\n", a1)
	fmt.Printf("a2: %v\n", a2)
	fmt.Printf("a3: %v\n", a3)
}

运行结果

a1: 0
a2: 100
a3: 2

3、基本数据类型

3.1、基本数据类型

序号 类型和描述
1 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
3 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型

3.2、数字类型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号 类型和描述
1 uint8 无符号 8 位整型 (0 到 255)
2 uint16 无符号 16 位整型 (0 到 65535)
3 uint32 无符号 32 位整型 (0 到 4294967295)
4 uint64 无符号 64 位整型 (0 到 18446744073709551615)
5 int8 有符号 8 位整型 (-128 到 127)
6 int16 有符号 16 位整型 (-32768 到 32767)
7 int32 有符号 32 位整型 (-2147483648 到 2147483647)
8 int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

3.3、浮点型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号 类型和描述
1 uint8 无符号 8 位整型 (0 到 255)
2 uint16 无符号 16 位整型 (0 到 65535)
3 uint32 无符号 32 位整型 (0 到 4294967295)
4 uint64 无符号 64 位整型 (0 到 18446744073709551615)
5 int8 有符号 8 位整型 (-128 到 127)
6 int16 有符号 16 位整型 (-32768 到 32767)
7 int32 有符号 32 位整型 (-2147483648 到 2147483647)
8 int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

3.4、其他数字类型

以下列出了其他更多的数字类型:

序号 类型和描述
1 byte 类似 uint8
2 rune 类似 int32
3 uint 32 或 64 位
4 int 与 uint 一样大小
5 uintptr 无符号整型,用于存放一个指针

3.5、布尔类型

go语言中的布尔类型有两个常量值:truefalse。布尔类型经常用在条件判断语句,或者循环语句。也可以用在逻辑表达式中。

3.6、字符串

3.6.1、基本定义

字符串字面量使用双引号 "" 或者反引号 ' 来创建

双引号 "":用来创建可解析的字符串,支持转义,但不能用来引用多行;

反引号 ':用来创建原生的字符串字面量,可能由多行组成,但不支持转义,并且可以包含除了反引号外其他所有字符

双引号创建可解析的字符串应用最广泛,反引号用来创建原生的字符串则多用于书写多行消息,HTML以及正则表达式。

package main

import "fmt"

func main() {
	var str string = "hello Caidd123"
	var title string = `
  ▄████  ▒█████  
 ██▒ ▀█▒▒██▒  ██▒
▒██░▄▄▄░▒██░  ██▒
░▓█  ██▓▒██   ██░
░▒▓███▀▒░ ████▓▒░
 ░▒   ▒ ░ ▒░▒░▒░ 
  ░   ░   ░ ▒ ▒░ 
░ ░   ░ ░ ░ ░ ▒  
      ░     ░ ░
	`

	fmt.Printf("str: %v\n", str)
	fmt.Printf("title: %v\n", title)
}

运行结果

str: hello Caidd123
title: 
  ▄████  ▒█████
 ██▒ ▀█▒▒██▒  ██▒
▒██░▄▄▄░▒██░  ██▒
░▓█  ██▓▒██   ██░
░▒▓███▀▒░ ████▓▒░
 ░▒   ▒ ░ ▒░▒░▒░
  ░   ░   ░ ▒ ▒░
░ ░   ░ ░ ░ ░ ▒
      ░     ░ ░

3.6.2、字符串链接

3.6.2.1、字符串支持 + 级联操作和+=追加操作

package main

import "fmt"

func main() {
	name := "Cadd123"
	age := "23"
	msg := name + " " + age
	fmt.Printf("输出结果: %v\n", msg)
	fmt.Println("-------------")
	msg = ""
	msg += name
	msg += " "
	msg += age
	fmt.Printf("输出结果: %v\n", msg)
}

运行结果

输出结果: Cadd123 23
-------------  
输出结果: Cadd123 23

golang 里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 gc 带来额外的负担,所以性能比较差

3.6.2.2、fmt.Sprintf()函数

package main

import "fmt"

func main() {
	name := "Caidd123"
	age := "23"
	msg := fmt.Sprintf("%s,%s", name, age)
	fmt.Printf("输出结果: %v\n", msg)
}

运行结果

输出结果: Caidd123,23

内部使用 []byte 实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了 interface,所以性能也不是很好

3.6.2.3、strings.Join()

package main

import (
	"fmt"
	"strings"
)

func main() {
	name := "Caidd123"
	age := "23"
	msg := strings.Join([]string{name, age}, ",")
	fmt.Printf("输出结果: %v\n", msg)
}

运行结果

输出结果: Caidd123,23

join会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小

3.6.3、转义字符

转义符 含义
\r 回车符(返回行首)
\n 换行符(直接跳到下一行的同列位置)
\t 制表符
\' 单引号
\" 双引号
\\ 反斜杠

3.6.4、常用方法

方法 介绍
len(str) 求长度
+或fmt.Sprintf 拼接字符串
strings.Split 分割
strings.contains 判断是否包含
strings.HasPrefix,strings.HasSuffix 前缀/后缀判断
strings.Index(),strings.LastIndex() 子串出现的位置
strings.Join(a[]string, sep string) join操作

3.6.5、byte和rune类型

组成每个字符串的元素叫做“字符”,可以通过遍历或者单个获取字符串元素获得字符。 字符用单引号(’)包裹起来,如:

package main

import "fmt"

func main() {
	var a = '华'
	var b = 'a'
	fmt.Printf("a: %v,%c\n", a, a)
	fmt.Printf("b: %v,%c\n", b, b)
}

运行结果

a: 21326,华
b: 97,a

Go 语言的字符有以下两种:

  1. uint8类型,或者叫 byte 型,代表了ASCII码的一个字符。
  2. rune类型,代表一个 UTF-8字符

当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32

Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。

4、运算符

4.1、算术运算符

运算符 描述
+ 相加
- 相减
* 相乘
/ 相除
% 求余

注意: ++(自增)和--(自减)在Go语言中是单独的语句,并不是运算符。

4.2、关系运算符

运算符 描述
== 检查两个值是否相等,如果相等返回 True 否则返回 False。
!= 检查两个值是否不相等,如果不相等返回 True 否则返回 False。
> 检查左边值是否大于右边值,如果是返回 True 否则返回 False。
>= 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
< 检查左边值是否小于右边值,如果是返回 True 否则返回 False。
<= 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。

4.3、逻辑运算符

运算符 描述
&& 逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。
! 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。

4.4、位运算符

位运算符对整数在内存中的二进制位进行操作。

运算符 描述
& 参与运算的两数各对应的二进位相与。 (两位均为1才为1)
| 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1)
^ 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1)
<< 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。
>> 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。

4.5、赋值运算符

运算符 描述
= 简单的赋值运算符,将一个表达式的值赋给一个左值
+= 相加后再赋值
-= 相减后再赋值
*= 相乘后再赋值
/= 相除后再赋值
%= 求余后再赋值
<<= 左移后赋值
>>= 右移后赋值
&= 按位与后赋值
|= 按位或后赋值
^= 按位异或后赋值

5、流程控制

5.1、if

5.1.1、语法

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
}

5.1.2、基本实例

5.1.2.1、根据布尔值f判断

import "fmt"

func if1() {
	var f = true
	if f {
		fmt.Println("f is true")
	}
	fmt.Printf("程序运行结束")
}

func main() {
	if1()
}

程序运行结果

f is true
程序运行结束

5.1.2.2、根据年龄判断是否成年

package main

import "fmt"

func if2() {
	var age = 23
	if age > 18 {
		fmt.Println("你是成年人")
	}
	fmt.Printf("程序运行结束")
}

func main() {
	if2()
}

程序运行结果

你是成年人
程序运行结束

5.1.2.3、初始变量可以声明在布尔表达式里面,注意它的作用域

func if3() {
	if age := 23; age > 18 {
		fmt.Println("你是成年人")
	}
	fmt.Printf("程序运行结束")
}

func main() {
	if3()
}

程序运行结果

你是成年人
程序运行结束

注意:不能使用0或非0表示真假

5.1.2.4、go语言if语句使用提示:

  1. 不需使用括号将条件包含起来
  2. 大括号{}必须存在,即使只有一行语句
  3. 左括号必须在ifelse的同一行
  4. if之后,条件语句之前,可以添加变量初始化语句,使用进行分隔

5.1.3、if else

判断一个人是否成年

func else1() {
	age := 16
	if age >= 18 {
		fmt.Println("你是成年人")
	} else {
		fmt.Println("你还未成年")
	}
}

在if前面添加执行语句

func else2() {
	if age := 16; age >= 18 {
		fmt.Println("你是成年人")
	} else {
		fmt.Println("你还未成年")
	}
}

5.1.4、if else if

func elseif1() {
	score := 65
	if score >= 90 {
		fmt.Println("A")
	} else if score > 75 {
		fmt.Println("B")
	} else {
		fmt.Println("C")
	}
}

在if前面添加执行语句

func elseif2() {
	
	if score := 65; score >= 90 {
		fmt.Println("A")
	} else if score > 75 {
		fmt.Println("B")
	} else {
		fmt.Println("C")
	}
}

5.1.5、if的嵌套

if 布尔表达式 1 {
   /* 在布尔表达式 1 为 true 时执行 */
   if 布尔表达式 2 {
      /* 在布尔表达式 2 为 true 时执行 */
   }
}

判断3个数的大小

package main

import "fmt"

// a>b a>c a
// b>a b>c b else c

func f1() {
	a := 100
	b := 200
	c := 3
	if a > b {
		if a > c {
			fmt.Println("a最大")
		}
	} else {
		if b > c {
			fmt.Println("b最大")
		} else {
			fmt.Println("c最大")
		}
	}
}

func main() {
	f1()
}

5.2、for

5.2.1、语法

for 初始语句;条件表达式;结束语句{
    循环体语句
}

5.2.2、基本实例1

循环输出1到10

func f() {
	for i := 1; i <= 10; i++ {
		fmt.Printf("I: %v\n", i)
	}
}

运行结果

I: 1
I: 2
I: 3
I: 4
I: 5
I: 6
I: 7
I: 8
I: 9
I: 10

5.2.3、初始条件写在外面

func f() {
	i := 1
	for ; i <= 10; i++ {
		fmt.Printf("I: %v\n", i)
	}
}

5.2.4、初始条件和结束条件都可以省略

func f() {
	i := 1 // 初始条件
	for i <= 10 {
		fmt.Printf("i: %v\n", i)
		i++ // 结束条件
	}
}

5.2.5、无限循环

for {
    循环体语句
}

5.2.6、注:

for循环可以通过breakgotoreturnpanic语句强制退出循环。

5.3、for range

Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:

  1. 数组、切片、字符串返回索引和值
  2. map返回键和值。
  3. 通道(channel)只返回通道内的值。

5.3.1、基本实例1:循环数组

func f() {
	var a = [5]int{1, 2, 3, 4, 5}
	for i, v := range a {
		fmt.Printf("i: %d, v: %v\n", i, v)
	}
}

func main() {
	f()
}

运行结果

i: 0, v: 1
i: 1, v: 2
i: 2, v: 3
i: 3, v: 4
i: 4, v: 5

5.3.2、循环字符串

func f() {
	var s = "好好学习,天天向上,up!up!up!"
	for i, v := range s {
		fmt.Printf("i: %d, v: %c\n", i, v)
	}
	// %c 按照字符输出
}

func main() {
	f()
}

运行结果

i: 0, v: 好
i: 3, v: 好
i: 6, v: 学
i: 9, v: 习
i: 12, v: ,
i: 13, v: 天
i: 16, v: 天
i: 19, v: 向
i: 22, v: 上
i: 25, v: ,
i: 26, v: u
i: 27, v: p
i: 28, v: !
i: 29, v: u
i: 30, v: p
i: 31, v: !
i: 32, v: u
i: 33, v: p
i: 34, v: !

5.3.3、循环切片

func f() {
	var s = []int{1, 2, 3, 4, 5}
	for i, v := range s {
		fmt.Printf("i, %d, v: %v\n", i, v)
	}
}

func main() {
	f()
}

5.3.4、循环map

func f() {
	m := make(map[string]string)
	m["name"] = "Caidd123"
	m["age"] = "23"
	m["email"] = "Caidd123@qq.com"
	for k, v := range m {
		fmt.Printf("k: %v, v: %v\n", k, v)
	}
}

func main() {
	f()
}

运行结果

k: age, v: 23
k: email, v: Caidd123@qq.com
k: name, v: Caidd123

5.4、switch

5.4.1、语法

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

5.4.2、基本实例1:判断手指

func switchDemo1() {
	finger := 3
	switch finger {
	case 1:
		fmt.Println("大拇指")
	case 2:
		fmt.Println("食指")
	case 3:
		fmt.Println("中指")
	case 4:
		fmt.Println("无名指")
	case 5:
		fmt.Println("小拇指")
	default:
		fmt.Println("无效的输入!")
	}
}

5.4.3、多条件匹配

Go语言规定每个switch只能有一个分支。

但一个分支可以有多个值,多个case值中间使用英文逗号分隔。

func switchDemo2() {
	day := 3
	switch day {
	case 1, 2, 3, 4, 5:
		fmt.Println("工作日")
	case 6, 7:
		fmt.Println("休息日")
	}
}

5.4.4、case可以是条件表达式

func switchDemo3() {
	age := 23
	switch {
	case age < 25:
		fmt.Println("好好学习吧")
	case age > 25 && age < 35:
		fmt.Println("好好工作吧")
	case age > 60:
		fmt.Println("好好享受吧")
	default:
		fmt.Println("活着真好")
	}
}

5.4.5、fallthrough

fallthrough可以可以执行满足条件的下一个case

func f3() {
	a := 100
	switch a {
	case 100:
		fmt.Println("100")
		fallthrough
	case 200:
		fmt.Println("200")
	case 300:
		fmt.Println("300")
	default:
		fmt.Println("other")
	}
}

运行结果

100
200

5.4.6、注意事项

  1. 支持多条件匹配
  2. 不同的 case 之间不使用 break 分隔,默认只会执行一个 case
  3. 如果想要执行多个 case,需要使用 fallthrough 关键字,也可用 break 终止。
  4. 分支还可以使用表达式,例如:a>10.

5.5、break

break语句可以结束forswitchselect的代码块。

break语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的forswitchselect的代码块上。

跳转到标签处

func f() {
label:
	for i := 0; i < 10; i++ {
		if i == 5 {
			break label
		}
		fmt.Printf("%v\n", i)
	}
	fmt.Println("end...")
}
func main() {
	f()
}

运行结果

0
1
2
3
4
end...

5.6、continue

continue只能用在循环中,在go中只能用在for循环中,它可以终止本次循环,进行下一次循环。

continue语句后添加标签时,表示开始标签对应的循环。

5.6.1、输出1到10之间的偶数

func f() {
	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			fmt.Printf("i: %v\n", i)
		}
	}
}
func main() {
	f()
}

运行结果

i: 0
i: 2
i: 4
i: 6
i: 8

5.6.2、跳转到label

func f() {
	for i := 0; i < 5; i++ {
	label:
		for j := 0; j < 5; j++ {
			if i == 2 && j == 2 {
				continue label
			}
			fmt.Printf("i=%d,j=%d\n", i, j)
		}
	}
}
func main() {
	f()
}

运行结果

i=0,j=0
i=0,j=1
i=0,j=2
i=0,j=3
i=0,j=4
i=1,j=0
i=1,j=1
i=1,j=2
i=1,j=3
i=1,j=4
i=2,j=0
i=2,j=1
i=2,j=3
i=2,j=4
i=3,j=0
i=3,j=1
i=3,j=2
i=3,j=3
i=3,j=4
i=4,j=0
i=4,j=1
i=4,j=2
i=4,j=3
i=4,j=4

5.7、goto

goto语句通过标签进行代码间的无条件跳转goto语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。 例如双层嵌套的for循环要退出时:

5.7.1、跳转到指定标签

func f() {
	a := 1
	if a == 1 {
		goto LABEL1
	} else {
		fmt.Println("other")
	}

LABEL1:
	fmt.Printf("next...")
}

func main() {
	f()
}

运行结果

next...

5.7.2、跳出双重循环

func f() {
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i == 2 && j == 2 {
				goto LABEL2
			}
		}
	}
LABEL2:
	fmt.Println("This is label2")
}

func main() {
	f()
}

运行结果

This is label2

6、数组

数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化

6.1、语法:

数组定义的语法如下:

var variable_name [SIZE] variable_type

variable_name:数组名称

SIZE:数组长度,必须是常量

variable_type:数组保存元素的类型

6.2、基本实例1:

package main

import "fmt"

func main() {
	var a [3]int    // 定义一个int类型的数组a,长度是3
	var s [2]string // 定义一个字符串类型的数组s,长度是2

	fmt.Printf("a: %T\n", a)
	fmt.Printf("s: %T\n", s)
}

运行结果

a: [3]int
s: [2]string

从上面运行结果,我们可以看出,数组和长度和元素类型共同组成了数组的类型。

6.3、初始化

初始化,就是给数组的元素赋值,没有初始化的数组,默认元素值都是零值,布尔类型是false,字符串是空字符串。

6.3.1、未初始化

package main

import "fmt"

func main() {
	var a [3]int    // 定义一个int类型的数组a,长度是3
	var s [2]string // 定义一个字符串类型的数组s,长度是2
	var b [2]bool

	fmt.Printf("a: %v\n", a)
	fmt.Printf("s: %v\n", s)
	fmt.Printf("b: %v\n", b)
}

运行结果

a: [0 0 0]
s: ["" ""]
b: [false false]

6.3.2、使用初始化列表

实例

package main

import "fmt"

func main() {
	var a = [3]int{1, 2, 3}
	var s = [2]string{"Caidd123", "CC"}
	var b = [2]bool{true, false}

	a1 := [2]int{1, 2} // 亦可用简短声明来类型推断

	fmt.Printf("a: %v\n", a)
	fmt.Printf("s: %v\n", s)
	fmt.Printf("b: %v\n", b)
	fmt.Printf("a1: %v\n", a1)
}

运行结果

a: [1 2 3]
s: [Caidd123 CC]
b: [true false]
a1: [1 2]

6.3.3、可省略数组长度

数组长度可以省略,使用...代替,更加初始化值得数量自动推断,例如:

package main

import "fmt"

func main() {
	var a = [...]int{1, 2, 3}
	var s = [...]string{"Caidd123", "CC"}
	var b = [...]bool{true, false}

	a1 := [...]int{1, 2} // 亦可用简短声明来类型推断

	fmt.Printf("a: %v\n", a)
	fmt.Printf("s: %v\n", s)
	fmt.Printf("b: %v\n", b)
	fmt.Printf("a1: %v\n", a1)
}

运行结果

a: [1 2 3]
s: [Caidd123 CC]
b: [true false]
a1: [1 2]

6.3.4、指定位置(索引值)初始化

可以通过指定所有的方式来初始化,未指定所有的默认未零值。

package main

import "fmt"

func main() {
	var a = [...]int{0: 1, 2: 2}
	var s = [...]string{1: "Cadd123", 2: "CC"}
	var b = [...]bool{2: true, 5: false}

	a1 := [...]int{1, 2} // 亦可用简短声明来类型推断

	fmt.Printf("a: %v\n", a)
	fmt.Printf("s: %v\n", s)
	fmt.Printf("b: %v\n", b)
	fmt.Printf("a1: %v\n", a1)
}

运行结果

a: [1 0 2]
s: [ Cadd123 CC]
b: [false false true false false false]
a1: [1 2]

6.4、访问数组元素

可以通过下标的方式,来访问数组元素。数组的最大下标为数组长度-1,大于这个下标会发生数组越界。

package main

import "fmt"

func main() {
	var a [2]int
	a[0] = 100
	a[1] = 200

	fmt.Printf("a[0]: %v\n", a[0])
	fmt.Printf("a[0]: %v\n", a[1])

	// 修改 a[0]  a[1]
	a[0] = 1
	a[1] = 2

	fmt.Println("-----------")

	fmt.Printf("a[0]: %v\n", a[0])
	fmt.Printf("a[0]: %v\n", a[1])
}

运行结果

a[0]: 100
a[0]: 200
-----------
a[0]: 1
a[0]: 2

6.5、根据数组长度遍历数组

可以根据数组长度,通过for循环的方式来遍历数组,数组的长度可以使用len函数获得。

实例

package main

import "fmt"

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

	for i := 0; i < len(a); i++ {
		fmt.Printf("a[%d]: %v\n", i, a[i])
	}
}

运行结果

a[0]: 1
a[1]: 2
a[2]: 3
a[3]: 4
a[4]: 5
a[5]: 6

6.6、使用for range遍历数组

还可以使用for range循环来遍历数组,range返回数组下标和对应的值

实例

package main

import "fmt"

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

	for i, v := range a {
		fmt.Printf("a[%d]: %v\n", i, v)
	}
}

运行结果

a[0]: 1
a[1]: 2
a[2]: 3
a[3]: 4
a[4]: 5
a[5]: 6

6.7、多维数组

Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)。

6.7.1、二维数组的定义

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
	fmt.Println(a[2][1]) //支持索引取值:重庆
}

运行结果

[[北京 上海] [广州 深圳] [成都 重庆]]
重庆

6.7.2、二维数组的遍历

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s\t", v2)
		}
		fmt.Println()
	}
}

运行结果

北京	上海	
广州	深圳	
成都	重庆	

6.7.3、注意

多维数组只有第一层可以使用...来让编译器推导数组长度。例如:

//支持的写法
a := [...][2]string{
	{"北京", "上海"},
	{"广州", "深圳"},
	{"成都", "重庆"},
}
//不支持多维数组的内层使用...
b := [3][...]string{
	{"北京", "上海"},
	{"广州", "深圳"},
	{"成都", "重庆"},
}

6.8、数组是值类型

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。

7、切片

前面我们学习了数组,数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:我们申请的长度太大浪费内存,太小又不够用。

鉴于上述原因,我们有了go语言的切片,可以把切片理解为,可变长度的数组,其实它底层就是使用数组实现的,增加了自动扩容功能。切片(Slice)是一个拥有相同类型元素的可变长度的序列。

7.1、语法

声明一个切片和声明一个数组类似,只要不添加长度就可以了

var identifier []type

切片是引用类型,可以使用make函数来创建切片:

//var slice1 []type = make([]type, len)

//也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中capacity为可选参数。

make([]T, length, capacity)

这里 len 是数组的长度并且也是切片的初始长度。

7.2、基本实例1:

package main

import "fmt"

func main() {
	var names []string
	var numbers []int
	fmt.Printf("names: %v\n", names)
	fmt.Printf("numbers: %v\n", numbers)
	fmt.Println(names == nil)
	fmt.Println(numbers == nil)
}

运行结果

names: []
numbers: []
true
true

7.3、切片的长度和容量

切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。

实例

package main

import "fmt"

func main() {
	var names = []string{"Caidd123", "CC"}
	var numbers = []int{1, 2, 3}

	fmt.Printf("len: %d cap: %d\n", len(names), cap(names))
	fmt.Printf("len: %d cap: %d\n", len(numbers), cap(numbers))

	var s1 = make([]string, 2, 3)
	fmt.Printf("len: %d cap: %d\n", len(s1), cap(s1))
}

运行结果

len: 2 cap: 2
len: 3 cap: 3
len: 2 cap: 3

7.4、初始化

切片的初始化方法很多,可以直接初始化,也可以使用数组初始化等。

7.4.1、切片如何切分

// 切片  按位置切
func test1() {
	var s1 = []int{1, 2, 3, 4, 5, 6}
	s2 := s1[0:3] // [)
	fmt.Printf("s2: %v\n", s2)
	s3 := s1[3:]
	fmt.Printf("s3: %v\n", s3)
	s4 := s1[2:5]
	fmt.Printf("s4: %v\n", s4)
	s5 := s1[:]
	fmt.Printf("s5: %v\n", s5)
}

运行结果

s2: [1 2 3]
s3: [4 5 6]
s4: [3 4 5]
s5: [1 2 3 4 5 6]

7.4.2、直接初始化

package main

import "fmt"

func main() {
	s := []int{1, 2, 3}
	fmt.Printf("s: %v\n", s)
}

运行结果

s: [1 2 3]

7.4.3、使用数组初始化

package main

import "fmt"

func main() {
	arr := [...]int{1, 2, 3}  //定义一个数组;自动推断长度
	s1 := arr[:]			//切他,从头切到尾
	fmt.Printf("s1: %v\n", s1)
}

运行结果

s1: [1 2 3]

7.4.4、切片表达式(使用数组的部分元素初始化)

切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片。 切片表达式中的low和high表示一个索引范围(左包含,右不包含即左闭右开),得到的切片长度=high-low,容量等于得到的切片的底层数组的容量。

package main

import "fmt"

func main() {
	arr := [...]int{1, 2, 3, 4, 5, 6}
	s1 := arr[2:5]
	fmt.Printf("s1: %v\n", s1)
	s2 := arr[2:]
	fmt.Printf("s2: %v\n", s2)
	s3 := arr[:3]
	fmt.Printf("s3: %v\n", s3)
}

运行结果

s1: [3 4 5]
s2: [3 4 5 6]
s3: [1 2 3]

7.4.5、空(nil)切片

一个切片在未初始化之前默认为 nil,长度为 0,容量为0.

package main

import "fmt"

func main() {
	var s1 []int
	fmt.Println(s1 == nil)
	fmt.Printf("len: %d, cap: %d\n", len(s1), cap(s1))
}

运行结果

true
len: 0, cap: 0

7.5、遍历

切片的遍历和数组的遍历非常类似,可以使用for循环索引遍历,或者for range循环。

7.5.1、for循环索引遍历

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	for i := 0; i < len(s1); i++ {
		fmt.Printf("s1[%d]: %v\n", i, s1[i])
	}
}

运行结果

s1[0]: 1
s1[1]: 2
s1[2]: 3
s1[3]: 4
s1[4]: 5

7.5.2、for range循环

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	for i, v := range s1 {
		fmt.Printf("s1[%d]: %v\n", i, v)
	}
}

运行结果

s1[0]: 1
s1[1]: 2
s1[2]: 3
s1[3]: 4
s1[4]: 5

7.6、切片元素的添加、删除和copy

切片是一个动态数组,可以使用append()函数添加元素,go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。由于,切片是引用类型,通过赋值的方式,会修改原有内容,go提供了copy()函数来拷贝切片

7.6.1、添加元素

package main

import "fmt"

func main() {
	s1 := []int{}
	s1 = append(s1, 1)
	s1 = append(s1, 2)
	s1 = append(s1, 3, 4, 5) // 添加多个元素
	fmt.Printf("s1: %v\n", s1)

	s3 := []int{3, 4, 5}
	s4 := []int{1, 2}
	s4 = append(s4, s3...) // 添加另外一个切片
	fmt.Printf("s4: %v\n", s4)
}

运行结果

s1: [1 2 3 4 5]
s4: [1 2 3 4 5]

7.6.2、删除元素

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	// 删除索引为2的元素
	s1 = append(s1[:2], s1[3:]...)
	fmt.Printf("s1: %v\n", s1)
}

运行结果

s1: [1 2 4 5]

公式:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

7.6.3、拷贝切片

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3}
	s2 := s1
	s1[0] = 100
	fmt.Printf("s1: %v\n", s1)
	fmt.Printf("s2: %v\n", s2)
	fmt.Println("----------")

	s3 := make([]int, 3)

	copy(s3, s1)

	s1[0] = 1

	fmt.Printf("s1: %v\n", s1)
	fmt.Printf("s3: %v\n", s3)
}

运行结果

s1: [100 2 3]
s2: [100 2 3]
-------------
s1: [1 2 3]
s3: [100 2 3]

从运行结果,我们看到赋值的情况下,原来的变量被修改了,使用copy函数,原来的变量没有被修改。

8、map

map是一种key:value键值对的数据结构容器。map内部实现是哈希表(hash)。

map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

8.1、语法

可以使用内建函数 make 也可以使用 map 关键字来定义 map

/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable = make(map[key_data_type]value_data_type)

map_variable:map变量名称

key_data_type:key的数据类型

value_data_type:值得数据类型

8.2、基本实例1:

下面声明一个保存个人信息的map

package main

import "fmt"

func main() {
	m1 := make(map[string]string)
	m1["name"] = "Caidd123"
	m1["age"] = "20"
	m1["email"] = "Caidd123@gmail.com"

	fmt.Printf("m1: %v\n", m1)

	m2 := map[string]string{
		"name":  "CC",
		"age":   "20",
		"email": "CC@gmail.com",
	}

	fmt.Printf("m2: %v\n", m2)
}

运行结果

m1: map[age:20 email:Caidd123@gmail.com name:Caidd123]
m2: map[age:20 email:CC@gmail.com name:CC]

8.3、访问map

可以通过下标key获得其值,例如:

package main

import "fmt"

func main() {
	m1 := make(map[string]string)
	m1["name"] = "Caidd123"
	m1["age"] = "20"
	m1["email"] = "Caidd123@gmail.com"

	name := m1["name"]
	age := m1["age"]
	email := m1["email"]
	fmt.Printf("name: %v\n", name)
	fmt.Printf("age: %v\n", age)
	fmt.Printf("email: %v\n", email)
}

运行结果

name: Caidd123
age: 20
email: Caidd123@gmail.com

8.4、判断某个键是否存在

go语言中有个判断map中键是否存在的特殊写法,格式如下:

value, ok := map[key]

如果ok为true,存在;否则,不存在。

实例

package main

import "fmt"

func main() {
	m1 := make(map[string]string)
	m1["name"] = "Caidd123"
	m1["age"] = "20"
	m1["email"] = "Caidd123@gmail.com"

	v, ok := m1["address"]
	if ok {
		fmt.Println("键存在")
		fmt.Println(v)
	} else {
		fmt.Println("键不存在")
	}
}

运行结果

键不存在

8.5、遍历

可以使用for range循环进行map遍历,得到key和value值。

8.5.1、遍历key

package main

import "fmt"

func main() {
	 m := make(map[string]string)
	 m["name"] = "Caidd123"
	 m["age"] = "20"
	 m["email"] = "Caidd123@gmail.com"

	 for key := range m{
		 fmt.Println(key)
	 }
}

运行结果

name
age
email

8.5.2、遍历key和value

package main

import "fmt"

func main() {
	 m := make(map[string]string)
	 m["name"] = "Caidd123"
	 m["age"] = "20"
	 m["email"] = "Caidd123@gmail.com"

	 for key, value := range m{
		 fmt.Println(key + ":" +value)
	 }
}

运行结果

name:Caidd123
age:20
email:Caidd123@gmail.com

9、指针

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。

类型指针不能进行偏移和运算。

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

9.1、指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。

9.2、语法

一个指针变量指向了一个值的内存地址。(也就是我们声明了一个指针之后,可以像变量赋值一样,把一个值的内存地址放入到指针当中。)

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type :为指针类型

var_name :为指针变量名

* :用于指定变量是作为一个指针。

9.3、基本实例1:

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

9.4、基本实例2:

package main

import "fmt"

func main() {
	var a int= 20   /* 声明实际变量 */
	var ip *int        /* 声明指针变量 */
	ip = &a  /* 指针变量的存储地址 */
	fmt.Printf("a 变量的地址是: %x\n", &a  )
	/* 指针变量的存储地址 */
	fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
	/* 使用指针访问值 */
	fmt.Printf("*ip 变量的值: %d\n", *ip )
}

运行结果

a 变量的地址是: c00000a0a8
ip 变量储存的指针地址: c00000a0a8
*ip 变量的值: 20

9.5、指向数组的指针

9.5.1、语法

var ptr [MAX]*int; 表示数组里面的元素的类型是指针类型

9.5.2、指向数组的指针实例:

package main

import "fmt"

const MAX int = 3

func main() {
	a := []int{ 1, 3, 5}
	var i int
	var ptr [MAX]*int;
	fmt.Println(ptr)   //这个打印出来是[<nil> <nil> <nil>]
	for  i = 0; i < MAX; i++ {
		ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
	}
	for  i = 0; i < MAX; i++ {
		fmt.Printf("a[%d] = %d\n", i,*ptr[i] )   //*ptr[i]就是打印出相关指针的值了。
	}
}

运行结果

[<nil> <nil> <nil>]
a[0] = 1
a[1] = 3
a[2] = 5
posted @   Caidd123  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示