go语言的基本语法
基础语法
1、注释
注释就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码。注释是开发人员一个非常重要的习惯,也是专业的一种表现。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。
注释只是为了提高可读性,不会被计算机编译。
2、变量
在计算机编程中,我们用变量来保存并管理很多数据,并用变量名来区分、识别和处理这些数据。
变量本质上是一种对内存地址的引用,让你能够把程序中准备使用的每一段数据都赋给一个简短、易于记忆的名字进行操作。
2.1、声明变量
和C语言一样,Go语言也是通过var关键字进行声明,不同的是变量名放在类型前,具体格式如下
var 变量名 变量类型
var x int
var s string
var b bool
fmt.Println(x) // 0
fmt.Println(s) // ""
fmt.Println(b) // false
如果声明多个变量,可以进行简写
// 声明多个相同类型变量
var x,y int
// 声明多个不同类型变量
var (
name string
age int
isMarried bool
)
2.2、变量赋值
变量赋值的3种方法
- 1,变量名=值
// 先声明再赋值
var x int
x = 10 // 不要 重复声明 : var x = 10
fmt.Println(x)
x = 20. // 重新赋值
// 直接声明赋值
// var y string= "hello yuan!"
var y = "hello yuan!"
fmt.Println(y)
// 声明赋值精简版
s := "hi,yuan!" // 1、编译器会自动根据右值类型推断出左值的对应类型,等同于var s = "hi,yuan!"。2、该变量之前不能声明,否则重复声明
fmt.Println(s)
// 一行声明赋值多个变量
var name,age = "yuan",22
- 2,变量名=变量名
var a = 100
var b = a // 变量之间的赋值是值拷贝
fmt.Println(a, b)
a = 200
fmt.Println(b)
- 3,变量名=值 + 值 (变量名)
var a, b = 10, 20
var c = a + b
fmt.Println(c)
var d = c + 100
fmt.Println(d)
2.3、匿名变量
匿名变量即没有命名的变量,在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。 匿名变量用一个下划线_表示。
a,b,c :=4,5,6
fmt.Println(a,b,c)
// 如果只想接受第个变量,可以对前两个变量匿名
_,_,x := 4,5,6
fmt.Println(x)
匿名变量不占用命名空间,不会分配内存
让代码非常清晰,基本上屏蔽掉了可能混淆代码阅读者视线的内容,从而大幅降低沟通的复杂度和代码维护的难度。
2.4、变量命名规则
1、变量名称必须由数字、字母、下划线组成。
2、标识符开头不能是数字。
3、标识符不能是保留字和关键字。
4、建议使用驼峰式命名,当名字有几个单词组成的时优先使用大小写分隔
5、变量名尽量做到见名知意。
6、变量命名区分大小写
go语言中有25个关键字,不能用于自定义变量名
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
还有30多个预定义的名字,用于内建的常量、类型和函数
// 内建常量:
true false iota nil
// 内建类型:
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
// 内建函数:
make len cap new append copy close delete
complex real imag
panic recover
3,语句分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分(不建议这样写)。
//var name = "yuan";var age = 18 // 不推荐
//fmt.Println(name)
//fmt.Println(age) // 不报错但是不推荐
// 推荐写法
var name = "yuan" // 换行即分隔符
var age = 18
fmt.Println(name)
fmt.Println(age)
4、基本数据类型
4.1、整形
字节了解:
字节(Byte):计算机中数据储存的单位。
位(bit):也叫作“比特”,计算机中数据储存的最小单位,因为在计算机中是以二进制的形式数据储存,所以每个位以“0”或“1”表示。
位和字节的关系是:8个位组成一个字节。
字节与位的关系:1Byte=8bit。
- 整形类型
| 具体类型 | 取值范围 |
| ------ | ---------------------------------------- |
| int8 | -128到127 |
| uint8 | 0到255 |
| int16 | -32768到32767 |
| uint16 | 0到65535 |
| int32 | -2147483648到2147483647 |
| uint32 | 0到4294967295 |
| int64 | -9223372036854775808到9223372036854775807 |
| uint64 | 0到18446744073709551615 |
| uint | 与平台相关,32位操作系统上就是uint32,64位操作系统上就是uint64 |
| int | 与平台相关,32位操作系统上就是int32,64位操作系统上就是int64 |
var x int
x = 9223372036854775809
fmt.Print(x) // overflows int
- 进制转换
// 十进制转化
var a int = 10
fmt.Printf("%d \n", a) // 10 占位符%d表示十进制
fmt.Printf("%b \n", a) // 1010 占位符%b表示二进制
fmt.Printf("%o \n", a) // 12 占位符%o表示八进制
fmt.Printf("%x \n", a) // a 占位符%x表示十六进制
// 八进制转化
var b int = 020
fmt.Printf("%o \n", b) // 20
fmt.Printf("%d \n", b) // 16
fmt.Printf("%x \n", b) // 10
fmt.Printf("%b \n", b) // 10000
// 十六进制转化
var c = 0x12
fmt.Printf("%d \n", c) // 18
fmt.Printf("%o \n", c) // 22
fmt.Printf("%x \n", c) // 12
fmt.Printf("%b \n", c) // 10010
4.2、浮点型
- float类型
float类型分为float32和float64两种类型,这两种浮点型数据格式遵循 IEEE 754 标准。
单精度浮点数占用4个字节(32位)存储空间来存储一个浮点数。而双精度浮点数使用 8个字节(64位)存储空间来存储一个浮点数。
单精度浮点数最多有7位十进制有效数字,如果某个数的有效数字位数超过7位,当把它定义为单精度变量时,超出的部分会自动四舍五入。双精度浮点数可以表示十进制的15或16位有效数字,超出的部分也会自动四舍五入。
浮点类型默认声明为float64。
var f1 float32 // float32: 单精度浮点型
f1 = 3.1234567890123456789
fmt.Println(f1, reflect.TypeOf(f1))
var f2 float64 // 双精度浮点型
f2 = 3.1234567890123456789
fmt.Println(f2, reflect.TypeOf(f2))
var f3 = 3.1234567890123456789
fmt.Println(f3, reflect.TypeOf(f2)) // 默认64
- 科学计数表示
var f1 = 2e10 // 即使是整数用科学技术表示也是浮点型
fmt.Println(f1,reflect.TypeOf(f1))
var f2 = 2e-2
fmt.Println(f2,reflect.TypeOf(f2))
4.3、布尔类型
布尔类型是最基本数据类型之一,只有两个值:true和false,分别代表逻辑判断中的真和假,主要应用在条件判断中。
package main
import (
"fmt"
"reflect"
)
func main() {
var b bool // 声明b是一个布尔类型
b = true
b = false // 该类型只有true和false两个值,分别代表真假两种状态
fmt.Println(b, reflect.TypeOf(b))
fmt.Println(1 == 1) // 比较运算符的结果是一个布尔值
// fmt.Println(1 == "1") // 报错,mismatched types不能比较
fmt.Println(3 > 1)
var name = "yuan"
var b2 = name == "rain"
fmt.Println(b2)
}
4.4、字符串
var s = "hello yuan"
fmt.Println(s)
单引号只能标识字符
- 字符串的基本操作
字符串在内存中是一段连续存储空间
注意:
1, 索引从零开始计数
2, go语言不支持负索引
var s = "hello yuan"
fmt.Println(s)
// (1)索引取值 slice[index]
a:= s[2]
fmt.Println(string(a))
// (2)切片取值slice[start:end], 取出的元素数量为:结束位置 - 开始位置;
b1:=s[2:5] //
fmt.Println(b1)
b2:=s[0:] // 当缺省结束位置时,表示从开始位置到整个连续区域末尾;
fmt.Println(b2)
b3:=s[:8] // 当缺省开始位置时,表示从连续区域开头到结束位置;
fmt.Println(b3)
// (3)字符串拼接
var s1 = "hello"
var s2 = "yuan"
var s3 = s1 + s2 // 生成一个新的字符串
fmt.Println(s3)
- 转义符
Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等,如下表所示。
转义符 | 含义 |
---|---|
\r | 回车符(返回行首) |
\n | 换行符(直接跳到下一行的同列位置) |
\t | 制表符 |
' | 单引号 |
" | 双引号 |
\ | 反斜杠 |
举个例子,我们要打印一个Windows平台下的一个文件路径:
package main
import "fmt"
func main() {
var s1 = "hi yuan\nhi,alvin"
fmt.Println(s1)
var s2 = "his name is \"rain\""
fmt.Println(s2)
var s3 = "D:\\next\\go.exe"
fmt.Println(s3)
}
- 多行字符串
Go语言中要定义一个多行字符串时,就必须使用反引号字符:
s1 := `第一行
第二行
第三行
`
fmt.Println(s1)
反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
- 字符串的常用方法
| 方法 | 介绍 |
| ----------------------------------- | ----------------------------- |
| len(str) | 求长度 |
| strings.ToUpper,strings.ToLower | 生成一个新的全部大写的字符串,生成一个新的全部小写的字符串 |
| strings.ReplaceAll | 生成一个新的原字符串被指定替换后的字符串 |
| strings.Contains | 判断是否包含 |
| strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
| strings.Trim、 | 去除字符串两端匹配的内容 |
| strings.Index(),strings.LastIndex() | 子串出现的位置 |
| strings.Split | 分割,将字符串按指定的内容分割成数组 |
| strings.Join(a[]string, sep string) | join操作,将数组按指定的内容拼接成字符串 |
package main
import (
"fmt"
"reflect"
"strings"
)
func main() {
s := "hello world"
// fmt.Println(len(s))
// strings.ToUpper 和 strings.ToLower
s1 := strings.ToUpper("Yuan")
s2 := strings.ToLower("Rain")
fmt.Println(s1, s2)
// strings.Trim
user := " yuan "
fmt.Println(len(user))
fmt.Println(strings.TrimLeft(user, " "))
fmt.Println(strings.TrimSpace(user))
fmt.Println(strings.Trim(user, " "))
s := "alvin,yuan,eric"
// strings.Index,strings.LastIndex
var index = strings.Index(s, "yuan!")
fmt.Println(index) // 未找到返回-1
var index2 = strings.LastIndex(s, "l")
fmt.Println(index2)
// strings.HasPrefix,strings.HasSuffix,strings.Contains(实现的依赖的就是strings.Index)
fmt.Println(strings.HasPrefix(s, "alv"))
fmt.Println(strings.HasSuffix(s, "eric"))
fmt.Println(strings.Contains(s, "eric"))
// strings.Split: 将字符串分割成数组
var ret2 = strings.Split(s, ",")
fmt.Println(ret2, reflect.TypeOf(ret2))
// strings.Join:将数组拼接成字符串
var ret3 = strings.Join(ret2, "-")
fmt.Println(ret3, reflect.TypeOf(ret3))
}
4.6、类型转换
Go语言中只有强制类型转换,没有隐式类型转换。该语法只能在两个类型之间支持相互转换的时候使用。
// (1)整型之间的转换
var a int8
a = 99
fmt.Println(int64(a), reflect.TypeOf(int64(a)))
fmt.Println(float64(a), reflect.Typ
// (2)string与int类型的转换
x := strconv.Itoa(98)
fmt.Println(x, reflect.TypeOf(x))
y, _ := strconv.Atoi("97")
fmt.Println(y, reflect.TypeOf(y))
// (3) Parse系列函数
// ParseInt
// 输入:1.数字的字符串形式 2.base,数字字符串的进制,比如:2进制、10进制。
// 3.bitSize的含义是⼤⼩限制,如果字符串转化的整形数据类型超过bitSize的最大值,那么输出的int64为bitSize的最大值,err就会显⽰数据超出范围。
i1, _ := strconv.ParseInt("1000", 10, 8)
println(i1)
i2, _ := strconv.ParseInt("1000", 10, 64)
println(i2)
f2, _ := strconv.ParseFloat("3.1415926", 64)
fmt.Println(f2, reflect.TypeOf(f2))
f1, _ := strconv.ParseFloat("3.1415926", 32)
fmt.Println(f1, reflect.TypeOf(f1))
// 返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。
b1, _ := strconv.ParseBool("true")
fmt.Println(b1, reflect.TypeOf(b1))
b2, _ := strconv.ParseBool("T")
fmt.Println(b2, reflect.TypeOf(b2))
b3, _ := strconv.ParseBool("1")
fmt.Println(b3, reflect.TypeOf(b3))
b4, _ := strconv.ParseBool("100")
fmt.Println(b4, reflect.TypeOf(b4))
5、运算符
一个程序的最小单位是一条语句,一条语句最少包含一条指令,而指令就是对数据做运算,我们已经学完基本数据类型了,知道如何构建和使用一些最简单的数据,那么我们能对这些数据做什么运算呢,比如fmt.Println(1+1)这条语句包含两个指令,首先是计算1+1的指令,1就是数据,+就是算术运算符中的相加,这样计算机就可以帮我们执行这个指令计算出结果,然后执行第二个指令,即将计算结果2打印在终端,最终完成这条语句。
5.1、算数运算符
运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 求余 |
5.2、关系运算符
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
5.3、逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 |
fmt.Println(2.1 > 1 || 3 == 2)
// 用户名为root或者年龄大于18岁
username := "root"
age := 16
ret := username == "root" || !(age < 18)
fmt.Println(ret)
5.4、赋值运算符
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
«= | 左移后赋值 |
»= | 右移后赋值 |
&= | 按位与后赋值 |
^= | 按位异或后赋值 |
= |
++ | 自增 |
---|---|
– | 自减 |
var a = 10
// 使a自加1
ret := a + 1
a = ret
// 使a自加1
a = a + 1
// 使a自加1
a += 1 // 赋值元算符
// 使a自加1
a++ // 注意:不能写成 ++ a 或 -- a 必须放在右边使用
// b := a++ // 此处为错误的用法,不能写在一行,要单独作为语句使用
fmt.Println(a)
5.5、运算符优先级
// 案例1
var a, b, c, d = 8, 6, 4, 2
ret := a + b*c/d
fmt.Println(ret)
// 案例2
x := 10
y := 1
x += 5*(1+2) + y
fmt.Println(x)
z := 1+2 > 3 || 1 == 1*5
fmt.Println(z)
6、输入输出函数
6.1、输出函数
fmt.Print有几个变种:
Print: 输出到控制台,不接受任何格式化操作 Println: 输出到控制台并换行 Printf : 只可以打印出格式化的字符串,只可以直接输出字符串类型的变量(不可以输出别的类型) Sprintf:格式化并返回一个字符串而不带任何输出
- (1)Print 和Println
Print和Println()函数可以打印出字符串或变量的值。
name := "yuan"
age := 24
fmt.Print(name, age)
fmt.Println("hello world")
fmt.Println(name)
fmt.Println(age)
fmt.Println(name, age)
fmt.Println("姓名:", name, "年龄:", age)
- (2)格式化输出(Printf)
Printf 根据格式说明符格式化并写入标准输出。Printf 只打印字符串
比如上面打印一个人的基本信息格式:
name := "yuan"
age := 24
isMarried := false
salary := 3000.549
fmt.Printf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Printf("姓名:%v 年龄:%v 婚否:%v 薪资:%v\n", name, age, isMarried, salary)
fmt.Printf("姓名:%#v 年龄:%#v 婚否:%#v 薪资:%#v\n", name, age, isMarried, salary)
%v:以默认的方式打印变量的值
%#v:相应值的Go语法表示
%T:打印对应值的类型
%+d :带符号的整型,%d 不带符号的整型
%o :不带零的八进制,%#o 带零的八进制
%x :小写的十六进制,%X 大写的十六进制,%#x 带0x的十六进制
%b :打印整型的二进制
%t :打印true 或 false
%s:输出字符串表示,%-5s 最小宽度为5(左对齐)
%f 小数点而无指数,默认精度为6
%e 科学计数法,如-1234.456e+78
%p 带0x的指针,%#p 不带0x的指针
// 整形和浮点型
fmt.Printf("%b\n", 12) // 二进制表示:1100
fmt.Printf("%d\n", 12) // 十进制表示:12
fmt.Printf("%o\n", 12) // 八进制表示:14
fmt.Printf("%x\n", 12) // 十六进制表示:c
fmt.Printf("%X\n", 12) // 十六进制表示:c
fmt.Printf("%f\n", 3.1415) // 有小数点而无指数:3.141500
fmt.Printf("%.3f\n", 3.1415) // 3.142
fmt.Printf("%e\n", 1000.25) // 科学计数法: 1.000250e+03,默认精度为6
// 设置宽度
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1001", "alvin", 100)
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1002", "zuibangdeyuanlaoshi", 98)
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1003", "x", 78)
```
- (3)Sprintf
Printf和Sprintf都是替换字符串,Printf是直接输出到终端,Sprintf是不直接输出到终端,而是返回该字符串
```
name := "yuan"
age := 24
isMarried := false
salary := 3000.549
info := fmt.Sprintf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Println(info)
```
## 6.2、输入函数
go语言fmt包下有三个函数,可以在程序运行过程中从标准输入获取用户的输入:
- (1)fmt.Scan
```
func Scan(a ...interface{}) (n int, err error)
```
Scan 从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
本函数返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因。
```
package main
import "fmt"
func main() {
var (
name string
age int
isMarried bool
)
fmt.Scan(&name, &age, &isMarried) // 输入类型不一致,按默认值
fmt.Printf("扫描结果 name:%s age:%d married:%t\t", name, age, isMarried)
}
```
- (2)fmt.Scanf
```
func Scanf(format string, a ...interface{})(n int, err error)
```
Scanf从标准输入扫描文本,根据format参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中。
本函数返回成功扫描的数据个数和遇到的任何错误。
```
// 案例1
var (
name string
age int
isMarried bool
)
fmt.Scanf("1:%s 2:%d 3:%t", &name,&age,&isMarried)
fmt.Printf("扫描结果 姓名:%s 年龄:%d 婚否:%t", name,age,isMarried)
// 案例2
var a, b int
fmt.Scanf("%d+%d", &a, &b)
fmt.Println(a + b)
```
- (3)fmt.Scanln
```
func Scanln(a ...interface{}) (n int, err error)
```
Scanln类似于Scan,它遇到换行立即停止扫描。
本函数返回成功扫描的数据个数和遇到的任何错误。
**Scanln和Scan的区别就是Scanln遇到换行立即结束输入,而Scan则会将换行符作为一个空白符继续下一个输入**
# 7、常量与itoa
## 7.1 常量
常量是⼀个简单值的标识符,在程序运⾏时,不会被修改的量。 在Python、Java编程规范中,常量⼀般都是全⼤写字母,但是在Golang中,⼤⼩写是具有⼀定特殊含义的,所以不⼀定所有常量都得全⼤写。
声明赋值方式与变量接近,通过const实现
const 常量名[数据类型] = value
```
数据类型可以忽略不写,Golang编译器会⾃动推断出数据类型。 在使⽤时,要注意以下⼏点:
数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型
满⾜多重赋值
常量只定义不使⽤,编译不会报错
常量可以作为枚举,常量组
常量组中如不指定类型和初始化值,则与上⼀⾏⾮空常量右值相同
显⽰指定类型的时候,必须确保常量左右值类型⼀致,需要时可做显⽰类型转换。
```
```
// (1)声明常量
const pai = 3.1415926
const e float64 = 2.7182818
fmt.Println(pai * pai)
// (2)常量也可以和变量一样一组一起声明
// const monday, tuesday, wednesday = 1, 2, 3
// 更推荐下面这种方式
const (
monday = 1
tuesday = 2
wednesday = 3
thursday = 4
friday = 5
saturday = 6
sunday = 7
)
const (
female = 0
male = 1
)
// ⼀组常量中,如果某个常量没有初始值,默认和上⼀⾏⼀致
const (
a int = 1
b
c = 2
d
)
fmt.Println(a, b, c, d)
```
## 7.2 iota计数器
iota是go语言的常量计数器,只能在常量的表达式中使用。 使用iota时只需要记住以下两点
```
1.iota在const关键字出现时将被重置为0。
2.const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。
```
```
const (
food = iota
cloth
bed
electric
)
fmt.Println(food, cloth, bed, electric)
const (
a = 1
b = iota
c = 6
d
e = iota
f
)
fmt.Println(a, b, c, d, e, f)
```
```
const (
b = 1 << (iota * 10)
kb
mb
gb
tb
pb
)
fmt.Println(b, kb, mb, gb, tb, pb)
```