Go 语言入门指南|青训营
1. Go语言的出现
在具体学习go语言的基础语法之前,我们来了解一下go语言出现的时机及其特点。
Go语言最初由Google公司的Robert Griesemer、Ken Thompson和Rob Pike三个大牛于2007年开始设计发明,他们最终的目标是设计一种适应网络和多核时代的C语言。所以Go语言很多时候被描述为“类C语言”,或者是“21世纪的C语言”,当然从各种角度看,Go语言确实是从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等诸多编程思想。但是Go语言更是对C语言最彻底的一次扬弃,它舍弃了C语言中灵活但是危险的指针运算,还重新设计了C语言中部分不太合理运算符的优先级,并在很多细微的地方都做了必要的打磨和改变。
2. go版本的hello world
在这一部分我们只是使用“hello world”的程序来向大家介绍一下go语言的所编写的程序的基本组成。
package main
import "fmt"
func main() {
// 终端输出hello world
fmt.Println("Hello world!")
}
1
2
3
4
5
6
和C语言相似,go语言的基本组成有:
- 包声明,编写源文件时,必须在非注释的第一行指明这个文件属于哪个包,如package main。
- 引入包,其实就是告诉Go 编译器这个程序需要使用的包,如import "fmt"其实就是引入了fmt包。
- 函数,和c语言相同,即是一个可以实现某一个功能的函数体,每一个可执行程序中必须拥有一个main函数。
- 变量,Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
- 语句/表达式,在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
- 注释,和c语言中的注释方式相同,可以在任何地方使用以 // 开头的单行注释。以 /* 开头,并以 */ 结尾来进行多行注释,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。 需要注意的是:标识符是用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母和数字、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public); 标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected)。
3. 数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
3.0 定义变量
声明变量的一般形式是使用 var 关键字,具体格式为:var identifier typename。如下的代码中我们定义了一个类型为int的变量。
package main
import "fmt"
func main() {
var a int = 27
fmt.Println(a);
}
1
2
3
4
5
6
3.0.1 如果变量没有初始化
在go语言中定义了一个变量,指定变量类型,如果没有初始化,则变量默认为零值。零值就是变量没有做初始化时系统默认设置的值。
3.0.2 如果变量没有指定类型
在go语言中如果没有指定变量类型,可以通过变量的初始值来判断变量类型。如下代码
package main
import "fmt"
func main() {
var d = true
fmt.Println(d)
}
1
2
3
4
5
6
3.0.3 :=符号
当我们定义一个变量后又使用该符号初始化变量,就会产生编译错误,因为该符号其实是一个声明语句。
使用格式:typename := value
也就是说intVal := 1相等于:
var intVal int
intVal =1
1
2
3.0.4 多变量声明
可以同时声明多个类型相同的变量(非全局变量),如下图所示:
var x, y int
var c, d int = 1, 2
g, h := 123, "hello"
1
2
3
关于全局变量的声明如下: var ( vname1 v_type1 vname2 v_type2 ) 具体举例如下:
var (
a int
b bool
)
1
2
3
4
3.0.5 匿名变量
匿名变量的特点是一个下画线_,这本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。
使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。
示例代码如下:
func GetData() (int, int) {
return 10, 20
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
1
2
3
4
5
6
7
8
需要注意的是匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
3.0.6 变量作用域
作用域指的是已声明的标识符所表示的常量、类型、函数或者包在源代码中的作用范围,在此我们主要看一下go中变量的作用域,根据变量定义位置的不同,可以分为一下三个类型:
- 函数内定义的变量为局部变量,这种局部变量的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。这种变量在存在于函数被调用时,销毁于函数调用结束后。
- 函数外定义的变量为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,甚至可以使用import引入外部包来使用。全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。
- 函数定义中的变量成为形式参数,定义函数时函数名后面括号中的变量叫做形式参数(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。形式参数会作为函数的局部变量来使用。
3.2 指针
与C相同,Go语言让程序员决定何时使用指针。变量其实是一种使用方便的占位符,用于引用计算机内存地址。Go 语言中的的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址。
指针变量其实就是用于存放某一个对象的内存地址。
3.2.1 指针声明和初始化
和基础类型数据相同,在使用指针变量之前我们首先需要申明指针,声明格式如下:var var_name var-type,其中的var-type 为指针类型,var_name 为指针变量名, 号用于指定变量是作为一个指针。
代码举例如下:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
1
2
指针的初始化就是取出相对应的变量地址对指针进行赋值,具体如下:
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
1
2
3
4
3.2.2 空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil,也称为空指针。它概念上和其它语言的null、NULL一样,都指代零值或空值。
3.3 数组
和c语言相同,Go语言也提供了数组类型的数据结构,数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
3.3.1 声明数组
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
以上就可以定一个一维数组,我们举例代码如下:
var balance [10] float32 1
3.3.2 初始化数组
数组的初始化方式有不止一种方式,我们列举如下:
直接进行初始化:var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
通过字面量在声明数组的同时快速初始化数组:balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
数组长度不确定,编译器通过元素个数自行推断数组长度,在[ ]中填入...,举例如下: var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}和balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 数组长度确定,指定下标进行部分初始化:balanced := [5]float32(1:2.0, 3:7.0)
注意:
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小。
3.3.3 go中的数组名意义
在c语言中我们知道数组名在本质上是数组中第一个元素的地址,而在go语言中,数组名仅仅表示整个数组,是一个完整的值,一个数组变量即是表示整个数组。
所以在go中一个数组变量被赋值或者被传递的时候实际上就会复制整个数组。如果数组比较大的话,这种复制往往会占有很大的开销。所以为了避免这种开销,往往需要传递一个指向数组的指针,这个数组指针并不是数组。关于数组指针具体在指针的部分深入的了解。
3.3.4 数组指针
通过数组和指针的知识我们就可以定义一个数组指针,代码如下:
var a = [...]int{1, 2, 3} // a 是一个数组 var b = &a // b 是指向数组的指针 1 2 数组指针除了可以防止数组作为参数传递的时候浪费空间,还可以利用其和for range来遍历数组,具体代码如下:
for i, v := range b { // 通过数组指针迭代数组的元素
fmt.Println(i, v)
}
1
2
3
具体关于go语言的循环语句我们在以后再进行详细介绍。