二.基础语法
1.Hello World
`package main`
`import "fmt"//导入一个fmt用来输出的`
`func main() {`
`fmt.Println("Hello, 世界")//打印输出 hello world字符串!`
`}`
Go 是一门编译型语言,Go 语言的工具链将源代码及其依赖转换成计算机的机器指令(译注:静态编译)。Go 语言提供的工具都通过一个单独的命令 go
调用,go
命令有一系列子命令。最简单的一个子命令就是 run
。这个命令编译一个或多个以。.go
结尾的源文件,链接库文件,并运行最终生成的可执行文件。(本书使用$表示命令行提示符。)
$ go run helloworld.go
输出
Hello, 世界
2.命名
2.1命名
Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:heapSort和Heapsort是两个不同的名字。
Go语言中类似if和switch的关键字有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多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
内建常量: true false iota nil
内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 ***plex128 ***plex64
bool byte rune string error
内建函数: make len cap new append copy close delete
***plex real imag
panic recover
2.2声明
声明语句定义了程序的各种实体对象以及部分或全部的属性。Go语言主要有四种类型的声明语句:var、const、type和func,分别对应变量、常量、类型和函数实体对象的声明。
一个Go语言编写的程序对应一个或多个以.go为文件后缀名的源文件。每个源文件中以包的声明语句开始,说明该源文件是属于哪个包。包声明语句之后是import语句导入依赖的其它包,然后是包一级的类型、变量、常量、函数的声明语句,包一级的各种类型的声明语句的顺序无关紧要(译注:函数内部的名字则必须先声明之后才能使用)
3.变量
3.1变量的声明
var声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始值。变量声明的一般语法如下:
var 变量名字 类型 = 表达式
其中“类型”或“= 表达式”两个部分可以省略其中的一个。如果省略的是类型信息,那么将根据初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量。 数值类型变量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零值是空字符串,接口或引用类型(包括slice、指针、map、chan和函数)变量对应的零值是nil。数组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值。
Go语言的数据类型默认零值如下表所示:
数据类型 | 默认零值 |
---|---|
bool | false |
int | 0 |
float | (0+0i) |
string | “” |
array | 数组元素类型的零值 |
map | nil |
struct | 结构体中每个字段的零值 |
interface | nil |
function | nil |
需要注意的是,对于结构体和数组类型,默认零值是指结构体中每个字段或数组中每个元素的零值。而对于map类型,默认零值是nil,表示空映射。另外,所有类型的默认值均为其零值,除了bool类型没有默认值,注意在go语言中没有double类型。
零值初始化机制可以确保每个声明的变量总是有一个良好定义的值,因此在Go语言中不存在未初始化的变量。这个特性可以简化很多代码,而且可以在没有增加额外工作的前提下确保边界条件下的合理行为。例如:
var s string
fmt.Println(s) // ""
这段代码将打印一个空字符串,而不是导致错误或产生不可预知的行为。Go语言程序员应该让一些聚合类型的零值也具有意义,这样可以保证不管任何类型的变量总是有一个合理有效的零值状态。
也可以在一个声明语句中同时声明一组变量,或用一组初始化表达式声明并初始化一组变量。如果省略每个变量的类型,将可以声明多个类型不同的变量(类型由初始化表达式推导):
var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string
初始化表达式可以是字面量或任意的表达式。在包级别声明的变量会在main入口函数执行前完成初始化,局部变量将在声明语句被执行到的时候完成初始化。
一组变量也可以通过调用一个函数,由函数返回的多个返回值初始化:
var f, err = os.Open(name) // os.Open returns a file and an error
package main
import "fmt"
func main() {
var(
name string
age int
addr string
)
//string值默认为空
//int 默认值:0
fmt.Println(name,age,addr)
}
}
通过以上代码例子,我们可以看到go语言支持批量定义声明
3.2简短变量声明
在函数内部,有一种称为简短变量声明语句的形式可用于声明和初始化局部变量。它以“名字 := 表达式”形式声明变量,变量的类型根据表达式来自动推导。下面是lissajous函数中的三个简短变量声明语句:
anim := gif.GIF{LoopCount: nframes}
freq := rand.Float64() * 3.0
t := 0.0
package main
import "fmt"
func main(){
name:="yjh"
age :=18
//:= 自动推导
fmt.Println(name,age)
}
这是Go语言的推导声明写法,编译器会自动根据右值类型推断出左值的对应类型。
它可以自动的推导出一些类型,但是使用也是有限制的;
- 定义变量,同时显示初始化。
- 不能提供数据类型
- 只能在函数内部。不能随便到处定义
因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。var形式的声明语句往往是用于需要显式指定变量类型的地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
i := 100 // an int
var boiling float64 = 100 // a float64
var names []string
var err error
var p Point
和var形式声明语句一样,简短变量声明语句也可以用来声明和初始化一组变量:
i, j := 0, 1
但是这种同时声明多个变量的方式应该限制只在可以提高代码可读性的地方使用,比如for语句的循环的初始化语句部分。
var形式的声明语句往往是用于需要显示指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
当一个变量被声明之后,如果没有显示的给他赋值,系统自动赋予它类型的零值:
- 整型和浮点型变量的默认值为0和0.0
- 字符串变量的默认值为空字符串
- 布尔类型变量默认为false
- 切片、函数、指针变量的默认值为null
3.3变量赋值
-
Go语言中变量的赋值有以下几种方式:
-
直接赋值:将一个值赋给变量,格式为
变量名 = 值
。示例代码:var a int a = 10
-
声明同时赋值:可以在变量声明时同时赋值,格式为
var 变量名 类型 = 值
。示例代码:var b string = "hello"
也可以使用类型推导来简化代码,格式为
变量名 := 值
。示例代码:c := 20
-
多重赋值:可以同时给多个变量赋值,格式为
变量1, 变量2, ... = 值1, 值2, ...
。示例代码:var d, e int d, e = 30, 40
-
匿名变量赋值:使用
_
表示匿名变量,该变量不占用命名空间,不会分配内存,也不会引发“未使用变量”的错误。示例代码:_, f := 50, "world"
需要注意的是,赋值操作是从右往左进行的,即先计算等号右侧的表达式,然后将其赋值给左侧的变量。如果左右两侧的类型不匹配,编译器会报错。
-
3.4匿名变量
在Go语言中,匿名变量是使用_
表示的特殊变量,也被称为“占位符”或“空标识符”。匿名变量可以用在函数返回值、变量赋值、结构体中的成员赋值等场合。
使用匿名变量时,可以省略变量名,从而避免了对变量名的定义和使用,减少了代码量和命名冲突的可能性。同时,由于匿名变量不会分配内存,因此不会产生内存浪费的问题。
下面是一些匿名变量的使用场景:
- 函数返回值中的匿名变量
如果一个函数返回多个值,但是我们只需要其中的某些值,可以使用匿名变量来忽略不需要的返回值。示例代码:
func test() (int, string) {
return 10, "hello"
}
x, _ := test()
在上面的代码中,我们只需要函数test
返回的第一个值,因此用匿名变量_
来忽略第二个返回值。
- 忽略变量赋值中的某些值
有时候我们在进行变量赋值时,不需要使用某些返回值。这时候可以使用匿名变量来忽略这些值。示例代码:
var a int
a, _, _ = 1, 2, 3
在上面的代码中,我们用匿名变量_
来忽略了赋值语句中的第二个和第三个值。
- 结构体中的匿名字段
在结构体中,如果某个字段的类型是匿名的,那么该字段可以被称为匿名字段。可以通过结构体的匿名字段来访问该字段所属的结构体中的其他字段。示例代码:
type Person struct {
Name string
Age int
}
type Student struct {
Person
Score float64
}
var s Student
s.Name = "Tom"
s.Age = 18
s.Score = 90.5
在上面的代码中,结构体Student
中有一个匿名字段Person
,可以通过s.Name
和s.Age
来访问Person
结构体中的字段。
需要注意的是,在使用匿名变量时,虽然可以省略变量名,但是需要保留变量类型。否则会出现编译错误。
4.常量
在Go语言中,常量是指在程序运行期间不可改变的值。常量可以在编译期间计算,可以用于减少代码中的魔法数值、提高程序的可读性和可维护性。(注:在代码中,有些数字可能没有直观的含义,例如3600
表示一小时有多少秒,1024
表示2的10次方等等。这些数字被称为“魔法数值”,因为它们的意义不明确,需要在代码中寻找上下文来理解。)
在Go语言中,常量的定义使用关键字const
,常量名一般使用大写字母,多个单词之间使用下划线分隔。常量的值可以是数字、字符串、布尔值等。
常量定义的语法如下:
const name = value
下面是一些常量的例子:
const Pi = 3.14159
const Greeting = "Hello, World!"
const MaxInt32 = 1<<31 - 1
const Debug = false
在上面的例子中,我们定义了几个常量。Pi
常量的值是浮点数3.14159
,Greeting
常量的值是字符串"Hello, World!"
,MaxInt32
常量的值是整数2^31-1
,Debug
常量的值是布尔值false
。
常量还可以使用iota
来进行自增,它用于枚举、位运算等场景。iota
的初始值为0,每出现一次iota
,它的值就自增1。下面是一个枚举的例子:
package main
import "fmt"
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
func main() {
fmt.Println(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
}
在上面的例子中,我们使用iota
定义了一组枚举常量。Monday
的值是0
,Tuesday
的值是1
,以此类推。我们可以在fmt.Println
中打印这些常量的值,输出结果为0 1 2 3 4 5 6
。
总之,Go语言的常量可以提高程序的可读性和可维护性,可以减少代码中的魔法数值。在定义常量时,需要注意常量的命名规则和常量值的类型。
5类型
5.1Go语言中的类型
Go语言中的类型分为基本类型和复合类型两种。下面分别列举并说明。
基本类型 | 说明 |
---|---|
bool | 布尔型,值为true 或false
|
uint8 | 8位无符号整型,取值范围为0到255 |
uint16 | 16位无符号整型,取值范围为0到65535 |
uint32 | 32位无符号整型,取值范围为0到4294967295 |
uint64 | 64位无符号整型,取值范围为0到18446744073709551615 |
int8 | 8位有符号整型,取值范围为-128到127 |
int16 | 16位有符号整型,取值范围为-32768到32767 |
int32 | 32位有符号整型,取值范围为-2147483648到2147483647 |
int64 | 64位有符号整型,取值范围为-9223372036854775808到9223372036854775807 |
float32 | 32位浮点型,可以表示小数,精度为7位小数 |
float64 | 64位浮点型,可以表示小数,精度为15位小数 |
byte | 8位无符号整型,与uint8 相同 |
rune | 32位有符号整型,表示一个Unicode码点 |
uintptr | 无符号整型,用于存放一个指针的地址 |
复合类型 | 说明 |
---|---|
string | 字符串类型,由一串Unicode码点组成 |
array | 数组类型,元素类型相同,长度固定 |
slice | 切片类型,可以动态增长或缩小长度 |
map | 映射类型,键值对类型 |
struct | 结构体类型,由若干个字段组成,每个字段可以是任意类型 |
interface | 接口类型,可以包含任意类型的值 |
channel | 通道类型,用于在多个goroutine之间传递数据 |
需要注意的是,Go语言还支持类型别名,可以用type
关键字为现有的类型定义一个别名。例如:
type MyInt int // 将int类型定义为MyInt类型的别名
类型别名在代码维护和兼容性方面有一定的作用。
总的来说,Go语言的类型非常丰富,支持基本类型和复合类型,可以满足各种编程需求。同时,Go语言的类型系统非常强
5.2go语言的格式化输出
Go语言中的格式化输出是通过fmt
包实现的,它支持的格式化动词与C语言中的printf
函数类似,但也有一些不同之处。
下面是几个常用的格式化动词及其含义:
-
%d
:将整数格式化为十进制整数。 -
%f
:将浮点数格式化为十进制浮点数。 -
%e
:将浮点数格式化为科学计数法表示。 -
%s
:将字符串格式化为字符串。 -
%v
:将值的默认格式表示为字符串。
除了动词之外,还可以使用一些修饰符来控制输出格式,如:
-
%5d
:将整数格式化为长度为5的十进制整数。 -
%6.2f
:将浮点数格式化为长度为6,保留2位小数的十进制浮点数。
下面是一些例子,展示了如何使用格式化输出:
package main
import "fmt"
func main() {
x := 123
y := 3.14159
s := "hello"
fmt.Printf("x=%d, y=%f, s=%s\n", x, y, s)
fmt.Printf("x=%5d, y=%6.2f, s=%s\n", x, y, s)
}
输出:
x=123, y=3.141590, s=hello
x= 123, y= 3.14, s=hello
在上面的例子中,fmt.Printf
函数使用格式化字符串来格式化输出。在格式化字符串中,%d
表示整数,%f
表示浮点数,%s
表示字符串。使用%d
和%f
时,需要将要输出的值作为参数传递给fmt.Printf
函数,使用%s
时,直接将要输出的字符串作为参数传递即可。
值得注意的是,使用fmt.Printf
函数时,输出格式的字符串和参数列表中的值一一对应,如果不匹配会导致运行时错误。此外,fmt.Printf
函数还有其他一些格式化输出相关的函数,如fmt.Sprintf
、fmt.Fprintf
等,使用方式与fmt.Printf
类似。
6 go语言的作用域
Go语言中的作用域是指一个标识符(变量、常量、函数等)在程序中可被访问的范围。标识符的作用域由它在程序中的声明位置和所在代码块(包括函数、if语句、for循环等)的限制来确定。
Go语言中的作用域规则如下:
- 如果一个标识符在函数内声明,那么它的作用域只限于这个函数内部。
- 如果一个标识符在函数外声明,那么它的作用域可以是整个包内,也可以是跨包访问的公共标识符。
- 如果一个标识符在代码块内部声明(如if语句、for循环等),那么它的作用域只限于这个代码块内部。
- 在同一作用域内不能定义重名的标识符。
- 在嵌套的代码块中,内部的代码块可以访问外部的代码块中声明的标识符,但外部的代码块不能访问内部的代码块中声明的标识符。
下面是一些例子,展示了不同作用域的标识符的声明和使用方式:
package main
import "fmt"
var x = 10 // 在包级别声明一个变量x
func foo() {
var y = 20 // 在函数内部声明一个变量y
if x := 100; x > y { // 在if语句中声明一个变量x
fmt.Println(x) // x的作用域限于if语句块内
} else {
fmt.Println(y) // y的作用域限于foo函数块内
}
fmt.Println(x) // x可以在foo函数中访问
fmt.Println(y) // y可以在foo函数中访问
}
func main() {
foo()
fmt.Println(x) // x可以在整个包中访问
}
在上面的例子中,变量x
在包级别声明,可以在整个包中访问。变量y
在函数内部声明,只能在函数内部访问。在foo
函数内部,if语句中声明了一个新的变量x
,它的作用域限于if语句块内。在foo
函数内部,可以访问变量x
和y
。在main
函数中,可以访问包级别声明的变量x
。
总之,Go语言的作用域规则比较简单明了,但需要注意标识符的声明位置和所在代码块的限制,以避免出现作用域错误。