【Golang笔记01】Goland基础语法规则
Golang
笔记:快速学习Golang
基础语法规则
一、基础语法
1.1、环境安装
第一步需要安装go的运行环境,从官网下载安装包:https://golang.google.cn/dl/。
第二步需要安装go的开发工具,可以使用vscode
、goland
。这里推荐使用goland
。
1.2、hello world
package mainimport "fmt"func main() {fmt.Println("Hello World!")
}
- 入口程序的包一般是
main
。 - 入口函数名称是
main
。 func
关键字用于定义函数。import
关键字用于导入包。
1.3、package和import
package
关键字,用于声明包名称。声明包的语法:
package 包名称
import
关键字,用于导入包,导入包之后,就可以使用包里面的函数。导入的包语法:
import "包名称"
import 包别名 "包名称"
import
可以批量导入多个包,注意了,一行写一个包名称(没有逗号分隔符),语法:
import (
"包1"
"包2"
"包3"
)
只导入包,但是不使用包,只是为了调用包里面的init()
方法,语法:
import ("fmt"_ "math"
)
上面在导入包的时候,包名称前面添加一个【_】下划线,这就表示只导入包,不使用包。
1.4、注释
go
语言中的注释有下面几种:
- 单行注释:
//注释内容
。 - 多行注释:·
/*多行注释*/
1.5、变量
1.5.1、变量声明
golang
中定义变量,需要使用var
关键字。语法规则:
// 声明变量
var 变量名称 数据类型
// 声明并且赋值
var 变量名称 数据类型 = 变量值
变量也可以同时定义多个,语法:
var (变量名称1 数据类型变量名称2 数据类型变量名称3 数据类型
)// 案例代码
var (name stringage int
)
另外,go语言也支持变量类型的自动推导,也就是说,go语言可以根据右侧变量值的数据类型,自动推导出当前这个变量的数据类型。
变量名称 := 变量值
上面这个变量声明叫做:短变量声明并初始化。使用这种短变量的声明方式,那么就不能显示的声明变量类型,不需要使用var关键字,否则会编译失败。
1.5.2、变量赋值
go语言中,变量赋值语法如下:
package mainimport "fmt"func main() {fmt.Println("Hello World!")// 声明变量并且赋值var a string = "你好"fmt.Println(a)var b intb = 20fmt.Println(b)// 多个赋值var c stringvar d intc, d = "abc", 100fmt.Println(c, "---", d)// 短变量,注意:短变量不能重复赋值dd := "200"fmt.Println(dd)// 短变量多个赋值ee, ff := 100, truefmt.Println(ee, ff)// 变量交换,go中可以直接交换两个变量的值num1 := 100num2 := 200fmt.Println(num1, num2)num1, num2 = num2, num1fmt.Println(num1, num2)// 匿名变量 使用 _ 下划线表示匿名变量,匿名变量不会被接收,会直接被丢弃,go编译器不会分配内存空间n1, n2, _ := 1, 2, 3fmt.Println(n1, n2)
}
1.6、常量
go中使用const
定义常量,常量一旦定义赋值之后,就不可以对它的值进行修改,否则编译报错。
go中有一个内置常量,叫做:iota,表示无类型的整数序列。使用这个内置常量,可以生成一些序列号。
package mainimport "fmt"func main() {fmt.Println("Hello World!")const (num1 = iotanum2num3_num5)fmt.Println(num1, num2, num3, num5)const (n1 = iota << 1n2n3n4)fmt.Println(n1, n2, n3, n4)
}
1.7、运算符
go语言中,没有自增自减运算符,只有语句,语法:
变量名称++
变量名称--
需要注意的是,++
和--
只能够放在变量的后面,并且变量不能赋值给其他的变量。
package mainimport "fmt"func main() {fmt.Println("Hello World!")a := 1b := 2fmt.Println(a<<1, b>>2)fmt.Println("按位与运算:", a&b)// a &^ b 等价于 a & (^b) 先对b进行取反操作,在和 a 进行按位与运算fmt.Println("按位清除运算:", a&^b)a++b--fmt.Println("a++的结果:", a)fmt.Println("b--的结果", b)
}
1.8、输入输出
1.8.1、Stdout标准输出
输出有三种方式:
package mainimport ("os""syscall"
)func main() {var (Stdout = os.NewFile(uintptr(syscall.Stdout), "/dev/stdout"))// 控制台输出内容_, err := Stdout.WriteString("Hello, World!\n")if err != nil {return}
}
1.8.2、println()内置函数
go语言中内置了一个println()函数,可以直接调用这个函数输出内容到控制台。
package mainfunc main() {println("内置函数输出内容到控制台")
}
1.8.3、fmt.Println()函数
fmt包中也提供了输出函数,例如:Println()、Printf()函数。
package mainimport "fmt"func main() {fmt.Println("Hello World")fmt.Printf("%s==>%d", "111", 100)
}
1.8.4、Scan()输入
Scan()
接收输入数据,根据【空格、换行
】来接收输入数据。Scan()
方法会返回一个int整数,表示本次输入接收了几个数据。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var num int// 返回成功接收的数据个数n, err := fmt.Scan(&num)if err != nil {fmt.Println("输入错误")return}fmt.Println("接收的输入数据个数:", n)fmt.Printf("接收的数据num=%d", num)
}
Scan()
方法会直到接收数据个数和指定的&数量一样,才会结束执行,个数没有达到,则空格、回车都将表示一次数据的输入。
1.8.5、Scanln()输入
Scanln()
接收输入数据,通过【&
】符号获取到控制台输入的数据,根据【换行符
】判断是否结束输入数据。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var (num1 intnum2 int)// 返回成功接收的数据个数// 遇到换行,则停止接收输入数据n, err := fmt.Scanln(&num1, &num2)if err != nil {fmt.Println("输入错误")return}fmt.Println("接收的输入数据个数:", n)fmt.Printf("接收的数据num1=%d,num2=%d", num1, num2)
}
注意:输入的数据个数,需要和接收的数据个数相同,不相同则接收失败。
Scanln()
方法只要是回车,就表示结束接收数据。
1.8.6、Scanf()输入
Scanf()
根据指定的格式来接收数据,输入数据的格式必须和指定的格式一致,这样才可以正常接收到数据。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var (num1 intnum2 int)// 返回成功接收的数据个数// \n 表示回车换行n, err := fmt.Scanf("%d \n %d", &num1, &num2)if err != nil {fmt.Println("输入错误")return}fmt.Println("接收的输入数据个数:", n)fmt.Printf("接收的数据num1=%d,num2=%d", num1, num2)
}
1.8.7、缓冲
go语言中,如果对输入输出数据的性能有要求,则可以使用【bufio】缓冲流。
1.8.7.1、接收数据
接收数据,可以使用Reader和Scanner,其中Scanner提供了一个text()方法,可以逐行读取输入的数据。
Scanner读取的方式,案例代码:
package mainimport ("bufio""fmt""os"
)func main() {fmt.Println("请输入数据:")scan := bufio.NewScanner(os.Stdin)scan.Scan()// 逐行读取输入数据text := scan.Text()fmt.Println("text=", text)
}
Reader方式读取,案例代码:
package mainimport ("bufio""fmt""os"
)func main() {fmt.Println("请输入数据:")scan := bufio.NewReader(os.Stdin)// 读取到换行,则结束读取数据line, err := scan.ReadString('\n')if err != nil {fmt.Println("<UNK>")return}fmt.Println("line=", line)
}
1.8.7.2、输出数据
Writer是用于写入数据的,也就是输出数据到指定位置,例如:控制台、文件等。
package mainimport ("bufio""fmt""os"
)func main() {writer := bufio.NewWriter(os.Stdout)_, err := writer.WriteString("输出数据到控制台")if err != nil {fmt.Println(err)return}// 将缓冲区中的数据,刷新输出err = writer.Flush()if err != nil {fmt.Println(err)return}fmt.Println("\n数据输出完成!")
}
1.9、条件控制
1.9.1、if语句
go中也有if条件语句,语法格式:
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var (num1 intnum2 int)_, err := fmt.Scanf("%d %d", &num1, &num2)if err != nil {fmt.Println("输入错误。。。")return}fmt.Println("if条件表达式")if num1 > num2 {fmt.Println("num1 > num2")} else if num1 < num2 {fmt.Println("num1 < num2")} else {fmt.Println("num1 = num2")}
}
1.9.2、switch
语句
switch
语句,语法格式和java
中的类似,但是有一点区别。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var str string_, err := fmt.Scanf("%s", &str)if err != nil {fmt.Println("输入错误。。。")return}fmt.Println("switch条件表达式")switch str {case "1":fmt.Println("匹配case1")case "2":fmt.Println("匹配case2")case "3":fmt.Println("匹配case3")default:fmt.Println("case都没有匹配成功")}fmt.Println("switch执行结束")
}
fallthrough
关键字
如果想继续执行相邻case
的语句,那么可以使用fallthrough
关键字,这样go
就会执行fallthrough
关键字后面的那个case
语句。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var str string_, err := fmt.Scanf("%s", &str)if err != nil {fmt.Println("输入错误。。。")return}fmt.Println("switch条件表达式")switch str {case "1":fmt.Println("匹配case1")case "2":fmt.Println("匹配case2")// 执行完这个 case2,还会继续执行下一个相邻的 case3 语句块fallthroughcase "3":fmt.Println("匹配case3")default:fmt.Println("case都没有匹配成功")}fmt.Println("switch执行结束")
}
switch
还有另外一种语法,如下所示:
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var str string_, err := fmt.Scanf("%s", &str)if err != nil {fmt.Println("输入错误。。。")return}fmt.Println("switch条件表达式")// 这里switch后面不写条件,相当于是 switch true {}switch {case str == "1":fmt.Println("匹配case1")case str == "2":fmt.Println("匹配case2")// 执行完这个 case2,还会继续执行下一个相邻的 case3 语句块fallthroughcase str == "3":fmt.Println("匹配case3")default:fmt.Println("case都没有匹配成功")}fmt.Println("switch执行结束")
}
1.9.3、goto
关键字(不常用)
go
语言中提供了一个goto
关键字,可以将代码执行跳转到同一个函数中的对应label标签位置。goto
一般和label
标签一起使用才有意义。
label
标签可以给某一块代码做个标记,相当于是做了一个位置标记,使用goto
可以快速跳转到这个位置,执行标签之后的代码。
package mainimport "fmt"func main() {fmt.Println("请输入数据:")var str string_, err := fmt.Scanf("%s", &str)if err != nil {fmt.Println("输入错误。。。")return}fmt.Println("switch条件表达式")// 这里switch后面不写条件,相当于是 switch true {}switch {case str == "1":fmt.Println("匹配case1")case str == "2":fmt.Println("匹配case2")// 跳转到label01位置执行代码goto label01case str == "3":fmt.Println("匹配case3")default:fmt.Println("case都没有匹配成功")}// 当匹配到case2的时候,这一句代码就不执行了fmt.Println("switch执行结束")// label标签位置的代码,始终都会执行
label01:fmt.Println("跳转到label01位置,执行代码...")
}
1.10、循环控制
1.10.1、for语句
go中没有while语句,但是可以使用for语句来实现while语句的功能。for语法规则:
for 初始条件;循环条件;循环后条件 {// 代码
}// 这样就相当于while循环
for 循环条件 {// 代码
}for {// 死循环
}
案例代码:
package mainimport "fmt"func main() {// 打印九九乘法表for i := 1; i < 10; i++ {for j := 1; j <= i; j++ {fmt.Printf("%dx%d=%d\t", j, i, i*j)}fmt.Println()}
}
1.10.2、for…range语句
for range
可以更加方便的遍历一些可迭代的数据结构,例如:数组,切片,字符串,映射表,通道。语句格式如下:
// for...range 可以拿到 索引index 和 对应索引的value值
for index, value := range iterable {// 循环体
}
需要注意的是,不同的数据结构,for...range
遍历出来的东西是不一样的,具体情况具体分析。案例代码:
package mainimport "fmt"func main() {str := "Hello World"for index, value := range str {fmt.Printf("index=%d,value=%c\n", index, value)}
}
1.10.3、break和continue
go中也有break个continue两个关键字,break表示结束当前循环,continue表示结束本次循环,继续执行下一次循环。可以结合label标签一起使用,这样可以跳转到指定的循环之外。
1.11、数组和切片
go中提供了一个切片数据类型,和数组有点类似,但是两者有着很大的区别。切片是不定长的,当容量不足时候,切片可以自动扩容;而数组是定长的,长度一旦确定,就不能改变长度了。
1.11.1、数组
数组是定长的,长度不可改变。需要注意的是,go中数组的长度必须是一个const常量。
1.11.1.1、声明数组
go中数组的定义方式,语法如下:
// 第一种方式
var 数组名称 [长度]数据类型
// 第二种方式
数组名称 := [长度]数据类型{初始化数组的值}
// 第三种方式
数组名称 := new([长度]数据类型)
上面三种方式就是定义go的数组,第三种通过new
关键字定义的方式,返回的是一个数组指针。
package mainimport "fmt"func main() {var nums [5]intnums[1] = 1fmt.Println(nums)nums2 := [3]int{1, 2, 3}fmt.Println(nums2)nums3 := new([2]int)nums3[0] = 1fmt.Println(nums3)
}
1.11.1.2、使用数组
数组的使用,可以直接通过下标来访问,格式:
数组名称[下标]// 也可以使用 len 函数,访问数组中个数
len(arr)// 可以使用 cap 函数,访问数组容量,数组容量是等于数组长度的
cap(arr)
1.11.2.3、切割
go中数组还可以进行切割的操作,这和python中的切片有点类似。语法格式:
nums := [5]{1,2,3,4,5}
// 切割的语法
nums[起始下标:结束下标]// 起始下标如果省略,则表示从0开始计算
// 结束下标如果省略,则表示默认结束下标就是数组长度// 切割的区间范围是:左闭右开,含前不含后
案例代码:
package mainimport "fmt"func main() {var nums [5]intfor i := 0; i < 5; i++ {nums[i] = i + 1}// 切割ans := nums[0:2]fmt.Println(ans)ans = nums[:3]fmt.Println(ans)ans = nums[2:4]fmt.Println(ans)ans = nums[2:]fmt.Println(ans)
}
1.11.2、切片
1.11.2.1、切片的定义
go中切片的定义格式,和数组格式差不多,只不过切片不需要指定长度。格式如下:
var nums []int
nums := []int{1,2,3}
// 推荐使用这种方式定义切片
nums := make([]int, 0, 0)
nums := new([]int)
make方法有三个参数,分别是:切片类型,切片长度,切片容量大小,即可:make(类型,长度,容量)。
1.11.2.2、切片的使用
切片的使用需要通过append()
函数来完成。append()
函数有两个参数,分别是:
- 第一个参数:slice,表示要添加元素的目标切片。
- 第二个参数:elems,表示添加的元素,可以是多个。
(1)插入元素
切片插入元素就有三种方式,分别是:
- 尾部插入,这是最简单的方式,也是切片默认的方式。
- 中间插入,在切片中间的指定
i
位置,插入元素。 - 头部插入,在切片的头部,插入元素。
package mainimport "fmt"func main() {// 定义一个长度0、容量9的切片nums := make([]int, 0, 9)// 尾部插入元素nums = append(nums, 1, 2, 3, 4, 5, 6, 7, 8, 9)fmt.Println(nums)// 头部插入,这里有个注意点,头部插入,那么第一个参数就是插入元素的切片集合// 可以理解成,将nums切片插入到[]int{-1,0}切片的尾部,然后再将等到的新切片重新赋值给numsnums = append([]int{-1, 0}, nums...)fmt.Println(nums)nums = append([]int{}, 1, 2, 3, 4, 5, 6, 7, 8, 9)// 中间位置插入元素// 中间位置插入,那就需要多次嵌套使用append函数// 比如:要在中间3和4元素之间插入新的元素 88、99nums = append(nums[0:3], append([]int{88, 99}, nums[3:]...)...)fmt.Println(nums)
}
注意了,在使用append函数的时候,针对第二个参数,需要使用【...
】符号,这样才可以将切片元素解构出来。
(2)删除元素
切片删除元素,可以结合append函数来实现。常见的删除操作有下面几种:
- 删除头部元素。
- 删除中间元素。
- 删除尾部元素。
- 全部删除。
package mainimport "fmt"func main() {// 定义一个长度0、容量9的切片nums := make([]int, 0, 9)// 尾部插入元素nums = append(nums, 1, 2, 3, 4, 5, 6, 7, 8, 9)fmt.Println(nums)// 删除头部元素 1、2 两个元素nums = nums[2:]fmt.Println(nums)// 删除尾部元素 9nums = nums[:len(nums)-1]fmt.Println(nums)// 删除中间元素 5、6nums = append(nums[0:2], append([]int{}, nums[4:]...)...)fmt.Println(nums)// 全部删除nums = nums[:0]fmt.Println(nums)
}
(3)拷贝切片
切片可以拷贝。go中拷贝切片的时候,需要注意的一点是:必须保证目标切片的长度大于源切片的长度,否则拷贝失败。拷贝可以使用copy()
函数实现。copy有两个参数:
- 第一个参数:dest,表示目标切片。
- 第二个参数:src,表示源切片。
- 即:
copy(dest,src)
,返回一个int整数,表示拷贝成功的元素个数。
package mainimport "fmt"func main() {dest := make([]int, 5)src := make([]int, 0, 4)src = append(src, 1, 2, 3, 4)fmt.Println(dest, src)// 拷贝切片n := copy(dest, src)fmt.Println("n=", n)fmt.Println(dest, src)
}
(4)遍历切片
可以使用for、for...range
遍历切片。
1.11.2.3、多维切片
多维切片就和多维数组类似,但是切片的长度是不固定的。
// 定义一个长度为 5 的二维切片
// 二维切片中,每一个元素又是一个切片,长度是0
nums := make([][]int, 5)
1.11.2.4、拓展表达式
切片中,如果s2切片是来自于s1切片得到的,那么在对s2切片进行元素操作的时候,会将s1切片中的元素一起修改。这就是切片之间相互影响的问题。
package mainimport "fmt"func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}s2 := s1[3:4]fmt.Println(s1)fmt.Println(s2)// 修改切片,这样也会同时修改 s1 切片s2 = append(s2, 11)fmt.Println(s1)fmt.Println(s2)
}// 上面输出结果
[1 2 3 4 5 6 7 8 9]
[4]
// 可以看到 11 也加入到了 s1 切片里面,这说明,对s2进行修改,会影响s1
[1 2 3 4 11 6 7 8 9]
[4 11]
如何解决这个问题呢???
要想解决切片之间相互影响的问题,可以采用拓展表达式
来解决。拓展表达式
是在原先切片基础之上,新增了第三个max参数值,格式如下所示:
// 原先切片表达式
nums[low:high]// 这个就是拓展表达式,新增了第三个参数max
// (max-low) 表示当前切片的最大容量,当切片中的元素个数大于等于最大容量
// 那么,此时切片会重新分配底层数组存储切片元素,这样就不会影响原先的切片了
nums[low:high:max]
注意了,三个参数的大小关系必须是:low<=high<=max。
案例代码如下:
package mainimport "fmt"func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}// 这里通过拓展表达式来解决,切片相互影响的问题// low = 3、high = 4、max = 4// 所以最大容量:max - low = 4 - 3 = 1// 但是切片元素是:4-3 = 1// 切片元素>=最大容量,所以会重新分配底层数组s2 := s1[3:4:4]fmt.Println(s1)fmt.Println(s2)// 修改切片,这样也会同时修改 s1 切片s2 = append(s2, 11)fmt.Println(s1)fmt.Println(s2)
}// 上面输出结果
[1 2 3 4 5 6 7 8 9]
[4]
[1 2 3 4 5 6 7 8 9]
[4 11]
1.12、字符串
go中的字符串本质上是一个不可变
的字符数组
。
1.12.1、定义字符串
分为:原生字符串、普通字符串。
-
普通字符串使用【
双引号
】定义,不支持换行编写。 -
原生字符串使用【
反引号
】定义,支持多行编写,原生字符串中的内容都是原样输出的。
字符串可以使用循环来访问每一个字符,获取到的是对应字符的unicode
编码值。
package mainimport "fmt"func main() {str := "普通字符串"str2 := `原生字符串可以换行aaa`fmt.Println(str)fmt.Println(str2)
}
1.12.2、转换
字符串可以转换成字节切片,字节切片可以转换成字符串。可以使用下面的转换方式:
package mainimport "fmt"func main() {str := "普通字符串"bytes := []byte(str)fmt.Println(bytes)// 添加字节bytes = append(bytes, 110)fmt.Println(string(bytes))
}
string()
函数可以将字节切片转换成字符串。字符串可以通过【[]byte(字符串)
】方式转换成字节切片。
1.12.3、字符串长度
go语言中的字符串长度,不是字面上看到的字符长度,而是指存储字符串的字节数组长度。
package mainimport "fmt"func main() {str := "普通字符串"str2 := "123"fmt.Println(len(str), len(str2))
}
// 上面运行结果,unicode中一个中文字符占用3个字节,所以结果是 3*5=15
15 3
1.12.4、字符串拼接
go中字符串拼接,可以使用下面几种方式:
- 使用【
+
】加号拼接字符串。 - 使用字节切片拼接字符串。
- 使用
strings.Builder
方式拼接字符串,效率最高,推荐使用。Builder
提供了一个WriteString()
函数,通过这个函数,可以实现字符串的拼接写入功能。
package mainimport ("fmt""strings"
)func main() {str := "普通字符串"// 第一种方式str2 := str + ",拼接字符串"fmt.Printf(str2)fmt.Println()// 第二种方式bytes := []byte(str)bytes = append(bytes, 110, 111, 112)fmt.Printf(string(bytes))fmt.Println()// 第三种方式builder := strings.Builder{}builder.WriteString(str)builder.WriteString(",使用Builder拼接字符串")fmt.Println(builder.String())
}
1.13、映射表
go语言中,映射表是基于哈希表实现的,也就是常说的map映射容器,map是无序的。Go中规定,map的key键必须是可比较的,例如:string、int等类型都是可比较的。
1.13.1、定义map
go中定义map有两种方式,如下所示:
// 第一种方式:通过字面量的方式直接定义map
map[key类型]value类型{key1:val1,key2:val2,key3:val3
}// 第二种方式:通过内置函数make定义map
make(map[key类型]value类型,初始容量)
案例代码:
package mainimport "fmt"func main() {// 定义mapmap1 := map[string]int{"A": 1,"B": 2,}fmt.Println(map1)// 定义mapmap2 := make(map[int]string, 10)fmt.Println(map2)
}
1.13.2、map的使用
(1)访问元素
map访问元素,直接通过key获取即可。格式:
r1, r2 := map[key]// map访问元素之后,有两个返回值
// r1:第一个返回值表示获取到的元素值
// r2:第二个返回值表示是否存在key对应的value值
(2)存储元素
map中存储元素的时候,如果已经存在key值,则会覆盖value值。有一种情况除外,那就是key是math.NaN()
的时候,这种情况下,不会覆盖,而是一直新增。
package mainimport ("fmt""math"
)func main() {// 定义mapmap2 := make(map[float64]string, 10)fmt.Println(map2)map2[0] = "A"map2[1] = "B"// 重复key,覆盖valuemap2[1] = "C"fmt.Println(map2)// NaN情况除外map2[math.NaN()] = "D"map2[math.NaN()] = "E"fmt.Println(map2)
}
(3)删除元素
map要删除元素,可以使用delete()
函数。语法格式:
delete(map,key)
delete()
函数有两个参数,并且这个函数没有返回值。两个参数分别是:
- map:待删除元素的map。
- key:要删除map中的哪个key。
package mainimport ("fmt"
)func main() {// 定义mapmap2 := make(map[float64]string, 10)fmt.Println(map2)map2[0] = "A"map2[1] = "B"// 重复key,覆盖valuemap2[1] = "C"fmt.Println(map2)// 删除元素delete(map2, 1)fmt.Println(map2)
}
(4)遍历元素
map
可以使用for...range
循环遍历每一个key
和value
。
1.14、指针
Go里面保留了C语言中的指针这个概念。指针中有两个运算符号需要理解,分别是:
&
:取地址符号,表示获取某个变量对应的内存地址。*
:解引用符号,这个符号有2个作用。- 获取内容:当对指针使用【
*
】符号时候,表示获取指针对应地址中存储的内容。 - 声明指针:【
*
】号还可以声明指针变量,也就是将某个变量定义成指针类型。
- 获取内容:当对指针使用【
1.14.1、声明指针
go
中指针和C
语言中的指针用法类似。声明指针有下面几种方式:
package mainimport "fmt"func main() {num := 520// 定义指针p := &numfmt.Println("<指针地址>", p)// 通过 * 号定义指针var pp *intpp = &numfmt.Println("<指针地址>", pp)// 通过 * 和 new 关键字定义指针var pp2 *intpp2 = new(int)fmt.Println("<指针地址>", pp2)// 上面这种写法,可以简写成短变量写法pp3 := new(int)fmt.Println("<指针地址>", pp3)
}
1.14.2、读取指针内容
读取指针中对应的内容,可以使用【*
】解引用符号来实现。
package mainimport "fmt"func main() {num := 520// 定义指针p := &numfmt.Println("<指针地址>", p)// 读取指针中内容fmt.Println("<指针内容>", *p)
}
另外,Go
语言中,是不允许指针进行运算的,也就是禁止指针运算。
1.15、结构体
Go
语言中没有类与继承的概念,也丢弃了构造方法,虽然没有类的概念,但是提供了一个类似于class
类的概念,叫做:结构体。Go
中通过结构体,可以定义复杂的数据类型。
1.15.1、定义结构体
Go
语言中,定义结构体的语法格式:
type 结构体名称 struct {变量名称1 数据类型1变量名称2 数据类型2变量名称3 数据类型3
}package mainfunc main() {// 定义结构体type Person struct {name stringage inthobby []string}
}
1.15.2、创建结构体数据
结构体定义完成之后,接着就可以创建一个结构体,然后指定结构体中的数据内容了。
package mainimport "fmt"func main() {// 定义结构体type Person struct {name stringage inthobby []string}// 创建结构体pp := Person{name: "小朱",age: 18,hobby: []string{"C", "C++", "Java"},}fmt.Println(pp)
}
注意,创建结构体的时候,字段名称也可以省略不写,但是这样就必须按照结构体中定义的顺序,将每个字段都进行初始化赋值了。
1.15.3、组合
一个结构体中,可以将另一个结构体作为字段属性,这样就实现了结构体之间的组合使用。组合又可以分为显示组合
和匿名组合
。
(1)显示组合
显示组合
,是指在定义结构体的时候,需要主动定义字段名称以及结构体类型,如下所示:
package mainfunc main() {// 定义结构体type Person struct {name stringage inthobby []string}// 结构体type Student struct {// 这里就是显示组合// 将结构体 Person 定义在结构体 Student里面,并且指定了字段名称是 personperson Persongrade int}
}
显示组合的情况下,创建结构体和访问结构体的语法,需要按照下面的方式:
package mainimport "fmt"func main() {// 定义结构体type Person struct {name stringage inthobby []string}// 结构体type Student struct {// 这里就是显示组合// 将结构体 Person 定义在结构体 Student里面,并且指定了字段名称是 personperson Persongrade int}// 创建结构体student := Student{person: Person{name: "小朱",age: 18,},grade: 100,}// 访问结构体中的结构体属性fmt.Println(student.person.name)
}
(2)匿名组合
匿名组合
,是指在结构体中,不需要定义字段名称,只需要定义结构体的类型即可,如下所示:
package mainimport "fmt"func main() {// 定义结构体type Person struct {name stringage inthobby []string}// 结构体type Student struct {// 这里就是匿名组合// 将结构体 Person 定义在结构体 Student里面,没有指定字段名称Persongrade int}
}
匿名组合中,其实也是存在字段名称的,只不过字段名称就是默认类型名称了。
在匿名组合中,访问结构体中的另一个结构体属性时候,不需要指定字段名称,直接通过外部结构体变量名称,访问内部结构体的属性名称即可。
package mainimport "fmt"func main() {// 定义结构体type Person struct {name stringage inthobby []string}// 结构体type Student struct {// 这里就是匿名组合// 将结构体 Person 定义在结构体 Student里面,没有指定字段名称Persongrade int}// 创建结构体student := Student{// 注意,这里创建结构体的,字段名称就是结构体的类型名称Person: Person{name: "小朱",age: 18,hobby: []string{""},},grade: 100,}// 访问结构体中的结构体属性,不需要指定结构体的字段名称fmt.Println(student.name)
}
另外,需要注意的一点是,创建结构体的时候,匿名组合的方式下,结构体的字段名称就是结构体的类型名称。
1.15.4、结构体指针
结构体指针,其实就是将一个结构体的内存地址,使用一个指针变量来保持,这个指针就叫做:结构体指针。因为这个指针是指向的一个结构体。
p := &Person{字段名称1:值1,字段名称2:值2
}
上面代码中,变量【p
】就是一个结构体指针,指向的就是结构体Person
的内存地址。
在使用结构体指针的时候,不需要【*
】解引用符号就可以获取到结构体中的数据内容,这是因为Go
针对结构体指针,在编译的时候会转换为(*
p).age,相当于默认会加上【*
】解引用符号,这也算是Go
语言提供的一个语法糖。
package mainimport "fmt"func main() {// 定义结构体type Person struct {name stringage inthobby []string}// 结构体type Student struct {// 这里就是匿名组合// 将结构体 Person 定义在结构体 Student里面,没有指定字段名称Persongrade int}// 创建结构体pp := &Student{// 注意,这里创建结构体的,字段名称就是结构体的类型名称Person: Person{name: "小朱",age: 18,hobby: []string{""},},grade: 100,}// 等价于 (*pp).namefmt.Println(pp.name)fmt.Println((*pp).name)
}
到此,Go语言的基础语法就差不多学完了,后续需要继续进阶学习一下Go的其他知识点。
相关文章:
【Golang笔记01】Goland基础语法规则
Golang笔记:快速学习Golang基础语法规则 一、基础语法 1.1、环境安装 第一步需要安装go的运行环境,从官网下载安装包:https://golang.google.cn/dl/。 第二步需要安装go的开发工具,可以使用vscode、goland。这里推荐使用golan…...
STM32 ADC+DMA+TIM触发采样实战:避坑指南与源码解析
知识点1【TRGO的介绍】 1、TRGO的概述 TRGO:Trigger Output(触发输出),是定时器的一种功能。 它可以作为外设的启动信号,比如ADC转换,DAC输出,DMA请求等。 对于ADC来说,可以通过…...
Gmsh 读取自定义轮廓并划分网格:深入解析与实践指南
一、Gmsh 简介 (一)Gmsh 是什么 Gmsh 是一款功能强大的开源有限元网格生成器,广泛应用于工程仿真、数值模拟以及计算机图形学等领域。它为用户提供了从几何建模到网格划分的一整套解决方案,能够有效处理复杂几何形状,生成高质量的二维和三维网格,满足多种数值方法的需求…...
Elasticsearch/OpenSearch 中doc_values的作用
目录 1. 核心作用 2. 适用场景 3. 与 index 参数的对比 4. 典型配置示例 场景 1:仅用于聚合,禁止搜索 场景 2:优化大字段存储 5. 性能调优建议 6. 底层原理 doc_values 是 Elasticsearch/OpenSearch 中用于优化查询和聚合的列式存储结…...
如何在 Windows 10 或 11 上使用命令提示符安装 PHP
我们可以在 Windows 上从其官方网站下载并安装 PHP 的可执行文件,但使用命令提示符或 PowerShell 更方便。 PHP 并不是一种新的或不为人知的脚本语言,它已经存在并被全球数千名网络开发人员使用。它以开源许可并分发,广泛用于 LAMP 堆栈中。然而,与 Linux 相比,它在 Wind…...
SZU 编译原理
总结自 深圳大学《编译原理》课程所学相关知识。 文章目录 文法语法分析自顶向下的语法分析递归下降分析LL(1) 预测分析法FIRST 集合FOLLOW 集合 文法 乔姆斯基形式语言理论: 表达能力:0型文法 > 1型文法 > 2型文法 > 3型文法。 0 型文法&am…...
灌区量测水自动化监测解决方案
一、方案背景 随着社会发展和人口增长,水资源需求不断增大。我国水资源总量虽然丰富,但时空分布不均,加之农业用水占比大且效率偏低,使得水资源短缺问题日益凸显。农业用水一直是我国的耗水大户,占全部耗水总量的60%以…...
CVE-2017-8046 漏洞深度分析
漏洞概述 CVE-2017-8046 是 Spring Data REST 框架中的一个高危远程代码执行漏洞,影响版本包括 Spring Data REST < 2.5.12、2.6.7、3.0 RC3 及关联的 Spring Boot 和 Spring Data 旧版本。攻击者通过构造包含恶意 SpEL(Spring Expression Language&…...
1基·2台·3空间·6主体——蓝象智联解码可信数据空间的“数智密码”
近日,由全国数据标准化技术委员会编制的《可信数据空间 技术架构》技术文件正式发布,标志着我国数据要素流通体系向标准化、规范化迈出关键一步。该文件从技术功能、业务流程、安全要求三大维度对可信数据空间进行系统性规范,为地方、行业及企…...
MySQL的存储过程
这一部分比较重要,加油!!!部分代码忘记保存了,嘻嘻,练习代码在最后,大家共勉!!! 通俗来讲,视图是死的,但是这个可以根据传入的参数不同…...
spring学习->sprintboot
spring IoC(控制翻转): 控制:资源的控制权(资源的创建,获取,销毁等) 反转:和传统方式不一样(用上面new什么),不用new让ioc来发现你用什么,然后我来给什么 DI:(依赖注入) 依赖:组件的依赖关系。如newsController依赖NewsServi…...
如何排查阻塞语句
文章目录 文档用途详细信息 文档用途 查询阻塞当前sql的语句,并结束阻塞语句。 详细信息 1、通过pg_stat_activity视图和pg_blocking_pids函数查找阻塞sql。 highgo# select pid,pg_blocking_pids(pid),wait_event_type,wait_event,query from pg_stat_activity…...
TIP-2021《SRGAT: Single Image Super-Resolution With Graph Attention Network》
推荐深蓝学院的《深度神经网络加速:cuDNN 与 TensorRT》,课程面向就业,细致讲解CUDA运算的理论支撑与实践,学完可以系统化掌握CUDA基础编程知识以及TensorRT实战,并且能够利用GPU开发高性能、高并发的软件系统…...
【AI学习】AI大模型技术发展研究月报的生成提示词
AI大模型技术发展研究月报生成提示词 请输出AI大模型技术发展研究月报,要求如下: —————————— 任务目标 在今天({{today}})往前连续 30 天内,检索已正式公开发表的、与AI大模型(参数量 ≥10B&am…...
深入理解 Git 分支操作的底层原理
在软件开发的世界里,Git 已经成为了版本控制的标配工具。而 Git 分支功能,更是极大地提升了团队协作和项目开发的效率。我们在日常开发中频繁地创建、切换和合并分支,但是这些操作背后的底层原理是怎样的呢?在之前的博客探秘Git底…...
泰迪杯特等奖案例深度解析:基于多模态融合与小样本学习的工业产品表面缺陷智能检测系统
(第九届泰迪杯数据挖掘挑战赛特等奖案例全流程拆解) 一、案例背景与核心挑战 1.1 工业质检痛点分析 在3C电子、汽车零部件等高端制造领域,产品表面缺陷(划痕、凹陷、氧化等)检测是质量控制的核心环节。传统人工目检存在效率低(平均检测速度3秒/件)、漏检率高(约15%)…...
Go语言爬虫系列教程 实战项目JS逆向实现CSDN文章导出教程
爬虫实战:JS逆向实现CSDN文章导出教程 在这篇教程中,我将带领大家实现一个实用的爬虫项目:导出你在CSDN上发布的所有文章。通过分析CSDN的API请求签名机制,我们将绕过平台限制,获取自己的所有文章内容,并以…...
轨道炮--范围得遍历,map巧统计
1.思路很难想,但代码一看一下就明白了,就是模拟时间,map存起来遍历也不受10*6影响 2.每次先统计点对应的直线,再动这个点,map一遍历实时更新ma统计max,AC!!!! https://www.luogu.com.cn/problem/P8695 #i…...
python中集合的操作
Python中的集合(Set)是一种无序、可变且元素唯一的数据结构,主要用于去重和数学运算。以下是核心操作分类: 1. 集合创建 大括号创建:s {1, 2, 3}(空集合必须用set())构造函数:…...
常见激活函数——作用、意义、特点及实现
文章目录 激活函数的意义常见激活函数及其特点1. Sigmoid(Logistic 函数、S型函数)2. Tanh(双曲正切函数)3. ReLU(Rectified Linear Unit修正线性单元)4. Softmax5. Swish(Google 提出ÿ…...
FC7300 Trigger MCAL配置引导
FC7300包含4个触发器选择(TRGSELs)。详细的连接信息将在章节中描述。Trigger Select (TRGSEL)源。TRGSEL模块允许软件为外设选择触发器源。 TRGSEL提供了一种极其灵活的机制,用于将各种触发器源连接到多个引脚/外设。 在TRGSEL中,每个控制寄存器最多支持4个输出触…...
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
组件导航 (Navigation)flutter项目搭建 接上一章flutter项目的环境变量配置并运行flutter 1.flutter创建项目并运行 flutter create fluter_hmrouter 进入ohos目录打开编辑器先自动签名 编译项目-生成签名包 flutter build hap --debug 运行项目 HMRouter搭建安装 1.安…...
WAS和Tomcat的对比
一、WAS和Tomcat的对比 WebSphere Application Server (WAS) 和 Apache Tomcat 是两款常用的 Java 应用服务器,但它们有许多显著的区别。在企业级应用中,它们扮演不同的角色,各自有其特点和适用场景。以下是它们在多个维度上的详细对比&…...
GPU Runtime Suspend 调试与验证:从 sysfs 到 perf 分析
选题背景:在基于 NXP i.MX8MP 平台调试 GPU 时,常常需要确认 Vivante GPU2D/ Vivante GPU2D/\uGPU3D 是否已经进入 runtime suspend ,以降为一篇完整的验证和分析步骤,适合用于实战调试与面试表达。 一、什么是 Runtime Suspend&a…...
响应式布局
布局方式 固定宽度布局:主流的宽度有960px/980px/1190px/1210px等。移动端用户需要缩放查看页面内容 流式布局:百分比设置相对宽度。在不同设备上都能完整显示。兼容性一般,可能发生错位 响应式布局:一套代码自动适配不同终端。检测设备信息,根据设备调整布局。用户体验最…...
简单入门RabbitMQ
本章将带大家来写一个简单的程序,使用 Java 创建RabbitMQ 的生产者和消费者 依赖引入 在 Maven 仓库中输入 amqp-client: 找到第一个 RabbitMQ Java Client ,点击进去找到一个合适的版本然后将依赖引入到我们项目中的 pom.xml 文件中。 …...
金属加工液展|切削液展|2025上海金属加工液展览会
2025上海金属加工液展览会 时间:2025年12月2-4日 地点:上海新国际博览中心 2025上海金属加工液展规划30000平方米展览规模,预设展位1200个,将为国内外加工液产业提供一个集“展示、合作、交易、发展”于一体的综合性平台&#…...
前端实现流式输出《后端返回Markdown格式文本,前端输出类似于打字的那种》
一、使用插件 插件名称:marked 版本:15.0.11 安装插件:npm install marked15.0.11 作用:marked 是一个用于将 Markdown 语法转换为 HTML 的 JavaScript 库 插件2名称:dompurify 版本:3.2.5 安装插件&…...
Python字符串常用方法详解
文章目录 Python字符串常用方法详解一、字符串大小写转换方法(常用)1. 基础大小写转换2. 案例:验证码检查(不区分大小写) 二、字符串查找与替换方法1. 查找相关方法2. 替换相关方法 三、字符串判断方法1. 内容判断方法 四、字符串分割与连接方…...
深度学习中的归一化:提升模型性能的关键因素
📌 友情提示: 本文内容由银河易创AI(https://ai.eaigx.com)创作平台的gpt-4-turbo模型辅助完成,旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证,建议读者通过官方文档或实践进一步确认…...
【C++】 —— 笔试刷题day_30
一、爱吃素 题目解析 这道题,简单来说就是给定两个数a和b,然后让我们判断a*b是否是素数。 算法思路 这道题还是比较简单的 首先,输入两个数a和b,这两个数的数据范围都是[1, 10^11];10的11次方,那a*b不就是…...
WebMvcConfigurer介绍-笔记
1.WebMvcConfigurer功能简介 org.springframework.web.servlet.config.annotation.WebMvcConfigurer 是 Spring MVC 提供的一个接口,用于自定义 Web 应用的配置。通过实现该接口,开发者可以灵活地添加拦截器(Interceptors)、配置…...
简单图像自适应亮度对比度调整
一、背景介绍 继续在刷对比度调整相关算法,偶然间发现了这个简单的亮度/对比度自适应调整算法,做个简单笔记记录。也许后面用得到。 二、自适应亮度调整 1、基本原理 方法来自论文:Adaptive Local Tone Mapping Based on Retinex for High Dynamic Ran…...
[SpringBoot]Spring MVC(2.0)
紧接上文,这篇我们继续讲剩下的HTTp请求 传递JSON数据 简单来说:JSON就是⼀种数据格式,有⾃⼰的格式和语法,使⽤⽂本表⽰⼀个对象或数组的信息,因此JSON本质是字符串. 主要负责在不同的语⾔中数据传递和交换 JSON的语法 1. 数据在 键值对(Key/Value) …...
GDB 高级调试技术深度解析
1. 引言 GNU调试器(GDB)是软件开发和逆向工程领域中不可或缺的工具。它为开发者提供了一个强大的环境,用于检查正在运行的程序或程序崩溃后产生的核心转储文件的内部状态。虽然许多开发者熟悉GDB的基本命令,如设置断点和单步执行,但GDB的真正威力在于其丰富的高级功能集。…...
【Kuberbetes】详谈网络(第三篇)
目录 前言 一、K8S的三种网络 1.1 Pod 内容器与容器之间的通信 1.2 同一个 Node 内 Pod 之间的通信 1.3 不同 Node 上 Pod 之间的通信 1.4 汇总 二、K8S的三种接口 三、VLAN 和 VXLAN 的区别 3.1 使用场景不同 3.2 支持的数量不同 3.3 是否记录到MAC地址表中…...
【科普】具身智能
一、具身智能的基本概念与理论框架 具身智能(Embodied Intelligence, EI)是指智能体通过物理身体与环境的实时交互,实现感知、决策和行动的能力。其核心思想是“智能源于身体与环境的互动”,而非仅仅依赖于抽象的计算或符号处理。…...
java -jar命令运行 jar包时如何运行外部依赖jar包
java -jar命令运行 jar包时如何运行外部依赖jar包 场景: 打包发不完,运行时。发现一个问题, java java.lang.NoClassDefFoundError: org/apache/commons/lang3/ArrayUtils 显示此,基本表明,没有这个依赖,如果在开发…...
Linux进程信号(一)之信号的入门
文章目录 信号入门1. 生活角度的信号2. 技术应用角度的信号3. 注意4. 信号概念5.用kill -l命令可以察看系统定义的信号列表6. 信号处理常见方式 信号入门 1. 生活角度的信号 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也…...
腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南
引言 在当今数据驱动的商业环境中,企业面临着海量数据处理和分析的挑战。腾讯云MCP(Managed Cloud Platform)提供的数据智能处理解决方案,为数据科学家和分析师提供了强大的工具集,能够显著简化数据探索、分析流程,并增强数据科学…...
曝光融合(Exposure Fusion)
一、背景介绍 图像融合算法里面,hdr图像进行融合,拓宽动态范围的操作非常常见。 常用的hdr融合算法通常有两类: 1、不同曝光的ldr图像先进行hdr融合,得到高bit位的hdr图像,再通过tonemaping算法得到结果如图像。 2、不…...
无人机减震模块运行与技术要点分析!
一、减震模块的运行方式 1. 多级减震结构 两级减震设计:采用第一级减震组件(如减震球、锥面减震垫)吸收高频振动,第二级减震组件(如减震环、负重物)进一步过滤低频振动。例如,通过硅胶减震球…...
CVPR2025 | 首个多光谱无人机单目标跟踪大规模数据集与统一框架, 数据可直接下载
论文介绍 题目:MUST: The First Dataset and Unified Framework for Multispectral UAV Single Object Tracking 期刊:IEEE/CVF Computer Vision and Pattern Recognition Conference 论文:https://arxiv.org/abs/2503.17699 数据&#x…...
嵌入式故障码管理系统设计实现
文章目录 前言一、故障码管理系统概述二、核心数据结构设计2.1 故障严重等级定义2.2 模块 ID 定义2.3 故障代码结构2.4 故障记录结构 三、故障管理核心功能实现3.1 初始化功能3.2 故障记录功能3.3 记录查询与清除功能3.4 系统自检功能 四、故障存储实现4.1 Flash 存储实现4.2 R…...
若依框架Consul微服务版本
1、最近使用若依前后端分离框架改造为Consul微服务版本 在这里分享出来供大家参考 # Consul微服务配置参数已经放置/bin/Consul微服务配置目录 仓库地址: gitee:https://gitee.com/zlxls/Ruoyi-Consul-Cloud.git gitcode:https://gitcode.c…...
【风控】用户特征画像体系
一、体系架构概述 1.1 核心价值定位 风控特征画像体系是通过多维度数据融合分析,构建客户风险全景视图的智能化工具。其核心价值体现在: 全周期覆盖:贯穿客户生命周期的营销、贷前、贷中、贷后四大场景立体化刻画:整合基础数据…...
【Java微服务组件】分布式协调P1-数据共享中心简单设计与实现
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 欢迎评论交流,感谢您的阅读😄。 目录 引言设计一个共享数据中心选择数据模型键值对设计 数据可靠性设计持久化快照 (…...
数据库--向量化基础
本文包含内容有: 向量化、SIMD的概念及关系SSE,AVX-512八种基础的SIMD操作,并用具体例子解释,给出伪代码。一、快速了解向量化、SIMD 1.1 向量化 向量化是指将原本需要循环处理的多个数据元素,通过一条指令同时处理多个数据,从而减少循环次数,提高计算效率。 传统方式…...
handsome主题美化及优化:10.1.0最新版 - 2
文章目录 前言基础设置优化开启全站 HTTPS添加 GZIP 压缩美化永久链接自定义后台路径启用 Emoji 支持 功能增强每日新闻自动更新文章嵌入外部网页时光机栏目配置自定义音乐播放器音量 自定义CSS配置文章标题居中显示标题背景美化文章版式优化LOGO 扫光特效头像动画效果图片悬停…...
JWT令牌
1. JWT概述 JWT即JSON Web Token,是一个开放标准,用于在各方之间安全地传输信息。并且JWT经过数字签名,安全性高。通俗来说,也就是以JSON形式作为Web应用中的令牌,用于信息传输,在数据传输过程中可以完成数…...