命令行语句
- 运行go文件
$ go run helloworld.go - 编译程序生成exe(可执行二进制文件)
$ go build helloworld.go
执行此文件
$ ./helloworld
Go语言结构
基础结构
Go语言的代码通过包(package)组织,包类似于其它语言里的库(libraries)或者模块(modules)。一个包由位于单个目录下的一个或多个.go源代码文件组成, 目录定义包的作用。每个源文件都以一条package声明语句开始,这个例子里就是package main, 表示该文件属于哪个包,紧跟着一系列导入import的包,之后是存储在这个文件里的程序语句。
main 包比较特殊。它定义了一个独立可执行的程序,而不是一个库。
一个函数的声明由 func 关键字、函数名、参数列表、返回值列表以及包含在大括号里的函数体组成。 Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特 定符号后的换行符转换为分号, 因此换行符添加的位置会影响Go代码的正确解析。举个例子,函数的左括号 { 必须和 func 函数声明在同一行上, 且位于末尾,不能独占一行,而在表达式 x + y 中,可在 + 后换行,不能在 + 前换行。
注释语句以//(单行)或/*...*/(多行)开头。
如果一个名字是在函数内部定义,那么它就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(必须是在函数外部定义的包级名字;包级函数名本身也是包级名字),那么它将是导出的,也就是说可以被外部的包访问.包本身的名字一般总是用小写字母。
变量与函数
声明一个变量有好几种方式,下面这些都等价:
s := ""
var s string
var s = ""
var s string = ""
实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的类型,否则使用隐式初始化。
var声明语句
可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始值。变量声明的一般语法如下:
var 变量名字 类型 = 表达式
其中类型或= 表达式两个部分可以省略其中的一个。如果省略的是类型信息,那么将根据初始化
表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量。 数值类型变
量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零值是空字符串,接口或引
用类型(包括slice、指针、map、chan和函数)变量对应的零值是nil。数组或结构体等聚合类型
对应的零值是每个元素或字段都是对应该类型的零值.
也可以在一个声明语句中同时声明一组变量,或用一组初始化表达式声明并初始化一组变量。如果省
略每个变量的类型,将可以声明多个类型不同的变量(类型由初始化表达式推导):
var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string
一组变量也可以通过调用一个函数,由函数返回的多个返回值初始化:
var f, err = os.Open(name) // os.Open returns a file and an error
- 简短变量声明
有一种称为简短变量声明语句的形式可用于声明和初始化局部变量。它以名字 := 表 达式形式声明变量,变量的类型根据表达式来自动推导。
因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。var形式的声 明语句往往是用于需要显式指定变量类型的地方,或者因为变量稍后会被重新赋值而初始值无关紧要 的地方。
请记住:=是一个变量声明语句,而=是一个变量赋值操作。也不要混淆多个变量的声明和元组的 多重赋值
i, j = j, i // 交换 i 和 j 的值
和普通var形式的变量声明语句一样,简短变量声明语句也可以用函数的返回值来声明和初始化变 量
f, err := os.Open(name)
简短变量声明左边的变量可能并不是全部都是刚刚声明的。如果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过的变量就只有赋值行为了。但注意简短变量声明语句中必须至少要声明一个新的变量.
Go 语言函数定义格式如下:
func 函数名称( [参数列表] ) [返回值类型] {
函数体
}
Go 函数可以返回多个值
指针
Go语言提供了指针。指针是可见的内存地址,&操作符可以返回一个变量的内存地址,并且*操作符可以获取指针指向的变量内容,但是在Go语言里没有指针运算,也就是不能像c语言里可以对指针进行加或减操作。
如果用var x int声明语句声明一个x变量,那么&x表达式(取x变量的内存地址)将产生一个指
向该整数变量的指针,指针对应的数据类型是*int.一般*p表达式读取指针指向的变量的值,这里为int类型的值,同时因为*p对应一个变量,所以该表达式也可以出现在赋值语句的左边,表示更新指针所指向的变量的值。任何类型的指针的零值都是nil.
x := 1
p := &x // p, of type *int, points to x
fmt.Println(*p) // "1"
*p = 2 // equivalent to x = 2
fmt.Println(x) // "2"
常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。常量的定义格式:
const identifier [type] = value
常量还可以用作枚举:
const (
Unknown = 0
Female = 1
Male = 2
)
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过。
iota,特殊常量,可以认为是一个可以被编译器修改的常量。iota在const关键字出现时将被重置为 0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次(iota可理解为 const语句块中的行索引)。
变量作用域
Go 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
在函数体内声明的变量称为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。
形式参数会作为函数的局部变量来使用。
数组
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
以下演示了数组初始化:
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}
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
// 将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
balance[4] = 50.0
以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体定义需要使用type和struct语句。struct语句定义一个新的数据类型,结构体中有一个或多个成员。type语句设定了结构体的名称。结构体的格式如下:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
如果要访问结构体成员,需要使用点号.操作符,格式为:
结构体.成员名"
切片
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
使用make()函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
切片是可索引的,并且可以由len()方法获取长度。
切片提供了计算容量的方法cap()可以测量切片最长可以达到多少。
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
拷贝切片copy方法,向切片追加新元素append方法。
map
map是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,map是无序的,遍历map时返回的键值对的顺序是不确定的。
在获取map的值时,如果键不存在,返回该类型的零值,例如int类型的零值是 0,string类型的零值是""。
map是引用类型,如果将一个map传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对map的修改会影响到所有引用它的变量。
可以使用内建函数make或使用map关键字来定义 Map:
/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)
其中KeyType是键的类型,ValueType是值的类型,initialCapacity是可选的参数,用于指定Map 的初始容量。Map的容量是指Map中可以保存的键值对的数量,当Map中的键值对数量达到容量时,Map会自动扩容。如果不指定initialCapacity,Go 语言会根据实际情况选择一个合适的值。
也可以使用字面量创建 Map:
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
删除元素:
// 删除键值对
delete(m, "banana")
控制语句
for循环
- Go语言只有for循环这一种循环语句。for循环三个部分不需括号包围。大括号强制要求, 左大括号必须和post语句在同一行。
for initialization; condition; post {
// zero or more statements
}
- for循环的这三个部分每个都可以省略,如果省略
initialization和post,分号也可以省略:
// a traditional "while" loop
for condition {
// ...
}
- 如果连 condition 也省略了,像下面这样:
// a traditional infinite loop
for {
// ...
}
这就变成一个无限循环,尽管如此,还可以用其他方式终止循环, 如一条break或return语句。
4. for 循环的另一种形式, 在某种数据类型的区间(range)上遍历,如字符串或切片。
for _, arg := range os.Args[1:] {
s += sep + arg
sep = " "
}
每次循环迭代,range产生一对值;索引以及在该索引处的元素值。这个例子不需要索引,
但range的语法要求, 要处理元素, 必须处理索引。一种思路是把索引赋值给一个临时变量,
如 temp, 然后忽略它的值,但Go语言不允许使用无用的局部变量(local variables),因为
这会导致编译错误。
Go语言中这种情况的解决方法是用 空标识符(blank identifier),即_(也就是下划线)。
空标识符可用于任何语法需要变量名但程序逻辑不需要的时候, 例如, 在循环里,丢弃不需要的循环
索引, 保留元素值。大多数的Go程序员都会像上面这样使用range和_写 echo 程序,因为隐
式地而非显式地索引os.Args,容易写对。
if语句
正如for循环一样,if语句条件两边也不加括号,但是主体部分需要加。if语句
的else部分是可选的,在if的条件为false时执行。
if n > 1 {
fmt.Printf("%d\t%s\n", n, line)
}
switch语句
- 带操作对象时,与C语言相同,但是case只执行一个
- 不带操作对象时,类似if...else
switch {
case x > 0:
return +1
default:
return 0
case x < 0:
return -1
}
错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error 类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
函数通常在最后的返回值中返回错误信息。使用errors.New可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
Go语言标准库
os包
os.Args
一个字符串(string)的切片(slice),用s[i]访问单个元素,用s[m:n]获取子序列,序列的元素数目为len(s)。(左闭右开)os.Args的第一个元素,os.Args[0], 是命令本身的名字;其它的元素则是程序启动时传给它的参数os.Open()
打开各个具名文件,并操作它们,os.Open函数返回两个值。第一个值是被打开的文件*os.File,返回的第二个值是内置error类型的值。如果err等于内置值nil,那么文件被成功打开。
bufio包
input := bufio.NewScanner(os.Stdin)
创建bufio.Scanner类型的变量,该变量从程序的标准输入中读取内容。每次调用input.Scan(),即读入下一行,并移除行末的换行符;读取的内容可以调用input.Text()得到。Scan函数在读到一行时返回true,不再有输入时返回false。
fmt包
fmt.Printf
对一些表达式产生格式化输出,类似C语言printf函数,默认情况下,Printf不会换行。fmt.Println
与fmt.Printf类似,会自动换行
%d 十进制整数
%x, %o, %b 十六进制,八进制,二进制整数。
%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
%t 布尔:true或false
%c 字符(rune) (Unicode码点)
%s 字符串
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 变量的自然形式(natural format)
%T 变量的类型
%% 字面上的百分号标志(无操作数)
net/http包
http.Get()
创建HTTP请求的函数,如果获取过程没有出错,那么会在resp这个结构体中得到访问的请求结果。resp的Body字段包括一个可读的服务器响应流.