go语言菜鸟教程:http://www.runoob.com/go/go-tutorial.html
实效go编程:http://docscn.studygolang.com/doc/effective_go.html
在线go运行环境:https://tour.golang.org/welcome/1
go的安装:参照这里在Windows上安装非常easy:http://www.runoob.com/go/go-environment.html
go中文标准库(Ctrl+f):http://docscn.studygolang.com/pkg/#stdlib
go英文包索引与搜索引擎:https://godoc.org/
/**********************************************************************************************************************/
gofmt
程序将Go程序按照标准风格缩进、 对齐,保留注释并在需要时重新格式化。若你想知道如何处理一些新的代码布局,请尝试运行 gofmt
;
分号并不在源码中出现。词法分析器会自动插入分号。可以概括为: “如果新行前的标记为语句的末尾,则插入分号”。左大括号不能放在下一行,如果这样做,就会在大括号前面插入一个分号,这可能引起不需要的效果。
Go不再使用 do
或 while
循环,只有一个更通用的 for
;switch
要更灵活一点;if
和 switch
像 for
一样可接受可选的初始化语句; 此外,还有一个包含类型选择和多路通信复用器的新控制结构:select
。 其语法也有些许不同:没有圆括号,而其主体必须始终使用大括号括住。
在变量与运算符间加入空格,程序看起来更加美观。
Go 语言数据类型
布尔型
布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
数字类型
整型 int 和浮点型 float,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
Go 也有基于架构的类型,例如:int、uint 和 uintptr。uint8uint16uint32uint64int8int16int32int64
浮点型:float32、float64。complex64(32 位实数和虚数)complex128(64 位实数和虚数)
字符串类型:
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
Go 语言变量
Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。
声明变量的一般形式是使用 var 关键字。
第一种,指定变量类型,声明后若不赋值,使用默认值,空指针为nil。
var name type
name = value
第二种,根据值自行判定变量类型
var name = value
var e, f = 123, "hello"
第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。这种不带声明格式的只能在函数体中出现。由系统自动推断,初始化声明。
name := value//只能在函数体中出现
// 例如
var a int = 10
var b = 10
c : = 10
第四种,多变量声明(重要)
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 //出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
值类型和引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值。当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误(a declared and not used) 但是全局变量是允许声明但不使用。
多变量(已申明)可以在同一行进行赋值,如:
a, b, c = 5, 7, "abc"
//未声明,用:=
a, b, c := 5, 7, "abc"
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
Go 语言常量
const identifier [type] = value
常量还可以用作枚举:
const (
Unknown = 0
Female = 1
Male = 2
)
神奇的iota
/*
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
iota 可以被用作枚举值:
*/
const (
a = iota
b = iota
c = iota
)
//简写形式
const (
a = iota
b
c
)
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
一个更神奇的用法:
package main
import "fmt"
const (
i=1<<iota // i=1<<0
j=3<<iota // j=3<<1
k // k=3<<2 12
l // j=3<<3 24
)
func main() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
}
//以上运行结果为:
i= 1
j= 6
k= 12
l= 24
逻辑运算符&& || !就是判断真假的
位运算符&, |, 和 ^
位运算符对整数在内存中的二进制位进行操作。对一个32位负数取非符号位的其他位a & 0x7fffffff
Go 语言 switch 语句
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。匹配项后面也不需要再加break,运行完后结束。
//Go 编程语言中 switch 语句的语法如下:
switch var1 {
case val1:
...
case val2:
...
default:
...
}
package main
import "fmt"
func main() {
/* 定义局部变量 */
var grade string = "B"
var marks int = 90
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
switch {
case grade == "A" :
fmt.Printf("优秀!\n" )
case grade == "B", grade == "C" :
fmt.Printf("良好\n" )
case grade == "D" :
fmt.Printf("及格\n" )
case grade == "F":
fmt.Printf("不及格\n" )
default:
fmt.Printf("差\n" );
}
fmt.Printf("你的等级是 %s\n", grade );
}
Go 语言 select 语句
select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。
以下描述了 select 语句的语法:
*每个case都必须是一个通信
*所有channel表达式都会被求值
*所有被发送的表达式都会被求值
*如果任意某个通信可以进行,它就执行;其他被忽略。
*如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
*否则:
*如果有default子句,则执行该语句。
*如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
}
Go 语言 for 循环
搭配break,continue使用;Go语言的For循环有3中形式,只有其中的一种使用分号:
//和 C 语言的 for 一样:
for init; condition; post { }
//和 C 的 while 一样,取代while:
for condition { }
//和 C 的 for(;;) 一样:
for { }
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:
for key, value := range oldMap {
newMap[key] = value
}
Go 语言函数
Go 语言函数定义格式如下:
func f(参数) (返回值) {
函数体
}
//返回多个值
func swap(x, y string) (string, string) {
return y, x
}
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
函数做为值使用:
package main
import (
"fmt"
"math"
)
func main(){
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
fmt.Println(getSquareRoot(9))
}
Go 语言匿名函数
func(参数列表) (返回值列表) {
函数体...
}
//直接使用,无参数直接加括号
func() int {
var i int =5
fmt.Printf("func 1\n")
return i
}()
//直接使用,有参数,在括号里加参数
func(arge int) {
fmt.Printf("func %d\n",arge)
}(2)
//也可以先赋给一个变量再调用
a := func() int {
fmt.Printf("func 3\n")
return 5
}
a()
Go 语言数组
- 数组是值。将一个数组赋予另一个数组会复制其所有元素。
- 切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。
- 特别地,若将某个数组传入某个函数,它将接收到该数组的一份副本而非指针。
- 数组的大小是其类型的一部分。类型
[10]int
和[20]int
是不同的。
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var name [SIZE] type
//var a [10] int
//数组初始化:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//如果忽略[]中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
Go 语言多维数组
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
//var threedim [5][10][4]int
var a = [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11} /* 第三行索引为 2 */
}
Go 语言向函数传递数组
//指定大小
void myFunction(param [10]int)
{
.
.
.
}
//不指定大小
void myFunction(param []int)
{
.
.
.
}
//例子
func getAverage(arr []int, size int) float32 {
var i,sum int
var avg float32
for i = 0; i < size;i++ {
sum += arr[i]
}
avg = float32(sum / size)
return avg;
}
Go 语言指针
//指针声明格式如下:
var name *type
//打印指针地址%x:
fmt.Printf("变量的地址: %x\n", &a )
//Go 空指针,它的值为 nil
if(ptr == nil)
Go 语言指针数组
声明整型指针数组(数组每个元素都是指针):
var ptr [3]*int;
Go 语言指向指针的指针
var ptr **int;
Go 语言指针作为函数参数
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 将 y 赋值给 x */
*y = temp /* 将 temp 赋值给 y */
Go 语言结构体
定义结构体和声明变量
//结构体定义需要使用 type 和 struct 语句。
//type 语句设定了结构体的名称。结构体的格式如下:
type my_type struct {
name1 type1
name2 type2
...
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
name := my_type {value1, value2...valuen}
//例子:
type Books struct {
title string
author string
subject string
book_id int
}
var Book1 Books
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
//结构体作为函数参数
func printBook( book Books ) {}
//结构体指针
var ptr *Books
Go 语言切片(Slice)
Go 语言切片是对数组的抽象。Go 数组的长度不可改变。切片("动态数组"),长度是不固定的
切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。
声明一个未指定大小的数组来定义切片:
var name []type
或使用make()函数来创建切片:
var slice1 []type = make([]type, len)
或者make([]T, length, capacity)//capacity是可选参数
也可以简写为:
slice1 := make([]type, len)
/************************************/
s :=[] int {1,2,3 } //直接初始化
s := arr[:] //用数组初始化切片
s := arr[startIndex:endIndex] //新切片
fmt.Printf("slice=%v\n",x) //输出切片2种方法 结果:slice=[0 0 0]
fmt.Println("slice=", x)
func printSlice(x []int){ //这个函数用于输出切片信息
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
if(numbers == nil){ //判断空切片
fmt.Printf("切片是空的")
}
切片截取
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
切片append()和 copy()
var numbers []int
/* 允许追加空切片 */
numbers = append(numbers, 0)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
Go 语言范围(Range)
//range一个切片,返回下标和对应项
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
//range也可以用在map的键值对上。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用来枚举Unicode字符串。
//第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
for i, c := range "go" {
fmt.Println(i, c)
}
//结果:
0 103
1 111
Go 语言Map(集合)
Map 是一种无序的键值对的集合。Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
/* 声明变量,默认 map 是 nil ,nil map 不能用来存放键值对*/
var myname map[ktype]vtype
/* 使用 make 函数,之后才能添加元素 */
myname = make(map[ktype]vtype)
//例子
var countryCapitalMap map[string]string
/* 创建集合 */
countryCapitalMap = make(map[string]string)
/* map 插入 key-value 对,各个国家对应的首都 */
countryCapitalMap["France"] = "Paris"
countryCapitalMap["Italy"] = "Rome"
countryCapitalMap["Japan"] = "Tokyo"
countryCapitalMap["India"] = "New Delhi"
/* 使用 key 输出 map 值 */
for country := range countryCapitalMap {
fmt.Println("Capital of",country,"is",countryCapitalMap[country])
}
/* 查看元素在集合中是否存在 */
captial, ok := countryCapitalMap["United States"]
/* 如果 ok 是 true, 则存在,否则不存在 */
if(ok){
fmt.Println("Capital of United States is", captial)
}else {
fmt.Println("Capital of United States is not present")
}
map中删除元素
//delete(), 参数为 map 和其对应的 key
/* 删除元素 */
delete(countryCapitalMap,"France");
Go 语言类型转换
type(myname)
var a int = 17
var b int = 5
var c float32
c = float32(a)/float32(b)
fmt.Printf("c 的值为: %f\n",c)
Go 语言new
Go提供了两种分配原语,即内建函数 new
和 make
。
new不会初始化内存,只会将内存置零。 也就是说,new(T)
会为类型为 T
的新项分配已置零的内存空间, 并返回它的地址,也就是一个类型为 *T
的值。
Go 语言make
内建函数 make(T,
args)
的目的不同于 new(T)
。
它只用于创建切片、映射和信道,并返回类型为 T
(而非 *T
)的一个已初始化 (而非置零)的值。 出现这种用差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。
make
只适用于映射、切片和信道且不返回指针。若要获得明确的指针, 请使用 new
分配内存(如下)。
var p *[]int = new([]int) // 分配切片结构;*p == nil;基本没用
var v []int = make([]int, 100) // 切片 v 现在引用了一个具有 100 个 int 元素的新数组
// 没必要的复杂:
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// 习惯用法:
v := make([]int, 100)
Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。
error类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
package main
import (
"fmt"
)
// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现`error`接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当被除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
有疑问加站长微信联系(非本文作者)