欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > 【Golang笔记01】Goland基础语法规则

【Golang笔记01】Goland基础语法规则

2025/5/18 1:22:36 来源:https://blog.csdn.net/qq_39826207/article/details/148010338  浏览:    关键词:【Golang笔记01】Goland基础语法规则

Golang笔记:快速学习Golang基础语法规则

一、基础语法

1.1、环境安装

第一步需要安装go的运行环境,从官网下载安装包:https://golang.google.cn/dl/。

第二步需要安装go的开发工具,可以使用vscodegoland。这里推荐使用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循环遍历每一个keyvalue

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的其他知识点。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词