1、变量与常量
1.1、变量声明
-
第一种,指定变量类型,如果没有初始化,则变量默认为零值
var num int num = 5
- 第二种,根据值自行判定变量类型
var name = "阿凡达"
- 第三种,省略var,使用:=,:=左边必须是新定义的变量,否则报错
f := 0.0043
- 多变量声明
var a,b,c = 1,2,3 d,e,f := 4,5,6
1.2、常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
- 声明
const PI = 3.14159
- iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
const ( A = iota // iota=0 A=0 B // iota=1 B=1 C // iota=2 C=2 D = "sky" // iota=3 D="sky" E // iota=4 E="sky" F = 39 // iota=5 F=39 G = iota // iota=6 G=6 H // iota=7 H=7 ) const ( J = iota // iota=0 J=0 )
2、流程结构
2.1、if 语句
package main
import "fmt"
func main() {
var num int
fmt.Print("请输入 num 的值:")
fmt.Scanln(&num)
if num >= 5 {
fmt.Println("num 大于等于5.")
} else if num <= 0 {
fmt.Println("num 小于等于0.")
} else {
fmt.Println("num 大于 0 小于 5.")
}
}
2.2、switch 语句
switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。
switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。
语法:
switch var1 {
case val1:
...
case val2:
...
default:
...
}
2.3、for 循环
语法
Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。
和 C 语言的 for 一样(1、先执行init,2、再判断 init 是否满足 condition 条件,3、执行循环体,4、执行 post ,给变量增值或减值):
for init; condition; post { 循环体 }
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
EXAMPLE:
//打印九九乘法表
func main() { for i := 1; i <= 9; i++ { for j := 1; j <= i; j++ { fmt.Printf("%d * %d = %d ", j, i, i*j) } fmt.Println() } }
3、数组
数组是存储一组数据类型相同且长度固定的数据序列。
3.1、声明数组
语法:var variable_name [SIZE] variable_type
var num_array [5]int
3.2、初始化数组
// 定义和赋值放一起 [size]表示数组的长度,[...]表示不指定数组长度,长度由赋值的长度自动推导
var num_array2 = [6]int{0, 1, 2, 3, 4, 5} var string_array = [...]string{"one", "two", "three", "four", "five", "six"}
3.3、访问数组元素
语法:array_name[index]
3.4、数组是值类型
package main
/*
1、值类型,赋值的时候,传递的是值的副本
int,float,string,array
2、引用类型,赋值的时候,传递的是内存地址
slice,map
数组的类型:[size]type
[6]int
[6]int
[6]string
*/
import "fmt"
func main() {
array1 := [6]int{0, 1, 2, 3, 4, 5}
array2 := [6]int{0, 1, 2, 3, 4, 5}
array3 := [...]string{"one", "two", "three", "four", "five", "six"}
// 数组的类型
fmt.Printf("%T
", array1) //[6]int
fmt.Printf("%T
", array2) //[6]int
fmt.Printf("%T
", array3) //[6]string
// 数组是值类型
array4 := array1
fmt.Println(array1) //[0 1 2 3 4 5]
fmt.Println(array4) //[0 1 2 3 4 5]
// array1的改变不会影响到array4,说明数组是值类型
array1[0] = 10
fmt.Println(array1) //[10 1 2 3 4 5]
fmt.Println(array4) //[0 1 2 3 4 5]
// 值类型比较的是值是否一样
fmt.Println(array1 == array2) // false
array1[0] = 0
fmt.Println(array1 == array2) // true
}
3.5、切片
package main
/*
slice就是不定长的数组
slice的两种定义方式:
1、 s1 := []int{}
[]中不填数组的长度,{}中可填数据,也可不填
[]type{}中间,则填要存储数据的类型,如 int,string,map[int]string
2、 s2 := make([]int, 0, 8)
通过内置函数make,make专门用来创建引用类型数据
slice的值存储的是内存地址
*/
import "fmt"
func main() {
s1 := []int{}
s2 := make([]int, 4, 8)
fmt.Println(s1) // []
fmt.Println(s2) // [0 0 0 0]
//使用 append 内置函数像 slice 中添加数据
fmt.Printf("s1 的内存地址是:%p;存储的值的内存地址是:%p
", &s1, s1)
s1 = append(s1, 1, 2, 3, 4)
fmt.Printf("添加数值后 s1 的内存地址是:%p;存储的值的内存地址是:%p
", &s1, s1)
}
3.6、深浅拷贝
package main
/*
1、深拷贝是值类型之间的拷贝,拷贝的是数值
2、浅拷贝是引用类型之间的拷贝,拷贝的是内存地址
copy(dst, src)函数:
相当于
for i:=0;i<len(dst);i++ {
dst[i]=src[i]
}
当然,也可使用切片,指定复制和粘贴的位置,如:
dst[2:] 从2的索引开始粘贴,直到结束
src[4:6] 复制索引4到6的数值
*/
import "fmt"
func main() {
s1 := []int{0, 1, 2, 3, 4}
//浅拷贝
//s1 的内存地址是:0xc00005a420,s1 的底层数组的内存地址是:0xc00008e030
//s2 的内存地址是:0xc00005a480,s2 的底层数组的内存地址是:0xc00008e030
//可以看到,s1和s2的存储的底层数组的内存地址是一样的,这就是浅拷贝
fmt.Printf("s1 的内存地址是:%p,s1 的底层数组的内存地址是:%p,s1 的值是:%d
", &s1, s1, s1)
s2 := s1
fmt.Printf("s2 的内存地址是:%p,s2 的底层数组的内存地址是:%p,s2 的值是:%d
", &s2, s2, s2)
fmt.Println("*****************************************************************")
//深拷贝
//s1 的内存地址是:0xc000004460,s1 的底层数组的内存地址是:0xc00000e360
//s3 的内存地址是:0xc000004580,s3 的底层数组的内存地址是:0xc00000e390
//可以看到,s1和s2的存储的底层数组的内存地址是不一样的,这就是深拷贝
fmt.Printf("s1 的内存地址是:%p,s1 的底层数组的内存地址是:%p,s1 的值是:%d
", &s1, s1, s1)
s3 := make([]int, 5, 5)
copy(s3, s1)
fmt.Printf("s3 的内存地址是:%p,s3 的底层数组的内存地址是:%p,s3 的值是:%d
", &s3, s3, s3)
}
4、string
4.1、strings包
package main
import (
"fmt"
"strings"
)
func main() {
// string 包的 replace 函数
str1 := "heeelloleeeworld"
str1 = strings.Replace(str1, "eee", "www", 1)
fmt.Println(str1)
// 判断字符串是否包含子字符串
fmt.Println(strings.Contains(str1, "eee"))
// 判断字符串中是否包含某个字符
fmt.Println(strings.ContainsAny(str1, "d"))
// 判断字符串是否以某个前缀开头
fmt.Println(strings.HasPrefix(str1, "heee"))
// 判断字符串是否以某个字符串结尾
fmt.Println(strings.HasSuffix(str1, "rld"))
// 以指定字符串切分字符串
fmt.Println(strings.Split(str1, "eee"))
}
4.2、strconv包
package main
import (
"fmt"
"strconv"
)
func main() {
str1 := "true"
b1, _ := strconv.ParseBool(str1)
fmt.Printf("%T
", b1)
// Atoi 把 string 转换成 int
str2 := "2020"
n1,_ := strconv.Atoi(str2)
fmt.Printf("%T
", n1)
//
n2 := strconv.Itoa(n1)
fmt.Printf("%T
", n2)
}
5、函数
5.1、函数的声明
格式:
parameters1和parameters2都是函数形式参数,value1和value2是返回值,
func funName(parameters1 type1, parameters2 type2) (value1 type3, value2 type4) {
//函数体
value1 := 4
value2 := 9
return
}
5.2、函数的可变参数
格式:
args ...type这里表示的可变参
func funcName([para type], args ...type) {
函数体
}
EXAMPLE:
func Sum(args ...int) { // 求和
sum := 0
for i := 0; i < len(args); i++ {
sum += args[i]
}
fmt.Println(sum)
}
5.3、函数的数据类型
我们试着打印下函数的数据类型看看
package main
import "fmt"
func main() {
//注意不带括号,带括号的函数表调用执行
fmt.Printf("fun11 函数的数据类型是:%T
", fun11) // func()
fmt.Printf("fun22 函数的数据类型是:%T
", fun22) // func(int, int, string)
fmt.Printf("fun33 函数的数据类型是:%T
", fun33) // func([]int) (int, int)
// 把函数赋值给一个变量
fun44 := fun11
fun44()
// 指向同一块内存地址
fmt.Printf("fun11 函数的内存地址:%p
", fun11)
fmt.Printf("fun44 函数的内存地址:%p
", fun44)
}
func fun11() {
fmt.Println("this is the test!")
}
func fun22(n1,n2 int, s1 string) {
}
func fun33(s1 []int) (int, int) {
return 0,0
}
5.4、函数的本质
函数定义时,内存中会开辟一段内存空间给函数用,函数名则指向这段内存空间的地址。
函数名不带括号,就表示这段内存地址;函数名带括号,就表示执行这个函数。
5.5、回调
回调是指,把一个函数当参数,传递给另外一个函数。
package main
/*
回调:把一个函数当做参数,传递给另外一个函数
*/
import "fmt"
func main() {
res := opt(1, 2, add)
fmt.Println(res)
}
// 加
func add(a, b int) int {
return a + b
}
//减
func sub(a, b int) int {
return a - b
}
// 算术运算
// 形参名+函数类型
func opt(a, b int, fun func(int, int) int) int {
return fun(a, b)
}
5.6、闭包
闭包函数:声明在一个函数中的函数
闭包:闭包函数总能访问外部函数的变量,外部函数的变量不会因调用而销毁
package main
import "fmt"
func main() {
zhaoyun := walk("赵云")
zhangfei := walk("张飞")
zhaoyun(2)
zhangfei(3)
zhangfei(1)
zhaoyun(2)
}
func walk(name string) func(int) {
total := 0
haha := func(hour int) {
total += hour
fmt.Printf("%s: 行军%d小时!
", name, hour)
fmt.Printf("%s: 一共行军%d小时!
", name, total)
}
return haha
}
6、指针
一个指针变量指向了一个值的内存地址。
*表示去指针数据
&表示取地址
定义:
// 定义一个存储整数类型的指针 var p1 *int // 定义一个双重指针 var p2 **int
使用:
// 给指针赋值 p1 = &a p2 = &p1
// 取出指针变量的值
*p1
*p2
空指针:
空指针用nil表示
7、结构体
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
定义:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
example:
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
}
访问结构体成员:
如果要访问结构体成员,需要使用点号 . 操作符
结构体.成员名