内容简介:Go(又称罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及Robert Griesemer, Rob Pike 和 Ken Thompson。Robert在开发Go之前是Google V8、Chubby和HotSpot JVM的主要贡献者;Rob主要是Unix、UTF-8、plan9的作者;Ken主要是B语言、C语言的作者、Unix之父。
语言介绍
Go(又称 Golang )是 Google 开发的一种 静态 强类型 、编译型、并发型,并具有垃圾回收功能的 编程语言 。
罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及 肯·汤普逊 (Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor、Russ Cox加入项目。Go是基于 Inferno 操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目,并在 Linux 及 Mac OS X 平台上进行了实现,后来追加了Windows系统下的实现。在2016年,Go被软件评价公司TIOBE 选为“TIOBE 2016 年最佳语言”
Robert Griesemer, Rob Pike 和 Ken Thompson。Robert在开发 Go 之前是Google V8、Chubby和HotSpot JVM的主要贡献者;Rob主要是Unix、UTF-8、plan9的作者;Ken主要是B语言、 C语言 的作者、Unix之父。
为什么会设计go语言
设计Go语言是为了解决当时Google开发遇到的以下这些问题:
大量的C++代码,同时又引入了 Java 和Python 成千上万的工程师 数以万计行的代码 分布式的编译系统 数百万的服务器
其主要有以下几个方面的痛点:
编译慢 失控的依赖 每个工程师只是用了一个语言里面的一部分 程序难以维护(可读性差、文档不清晰等) 更新的花费越来越长 交叉编译困难
所以,他们当时设计Go的目标是为了消除各种缓慢和笨重、改进各种低效和扩展性。Go是由那些开发大型系统的人设计的,同时也是为了这些人服务的;它是为了解决工程上的问题,不是为了研究语言设计;它还是为了让我们的编程变得更舒适和方便
语法规则介绍
包管理
package main import ( "fmt" "math/rand" ) func main() { fmt.Println("My favorite number is", rand.Intn(10)) } output: My favorite number is 1
每个 Go 程序都是由包组成的。
程序运行的入口是包 main
。
这个程序使用并导入了包 "fmt" 和 "math/rand"
。
按照惯例,包名与导入路径的最后一个目录一致。
例如, "math/rand"
包由 package rand 语句开始。
同一目录下只能用同一个包名
- 导出名
在导入了一个包之后,就可以用其导出的名称来调用它。
在 Go 中,首字母大写的名称是被导出的。
Foo 和 FOO 都是被导出的名称。名称 foo 是不会被导出的。
foo-相当于 php 的private , Foo 和 FOO 相当于public
package exports import "fmt" func Foo(){ fmt.Println("it is Foo") } func FOO(){ fmt.Println("it is FOO") foo() } func foo(){ fmt.Println("it is foo") }
函数
package main import "fmt" func add(x int, y int) int { return x + y } func add2(x, y int) int{ return x+y } func add3(x, y int) (z int){ z = x+y return } func main() { fmt.Println(add(42, 13)) fmt.Println(add2(42, 13)) fmt.Println(add3(42, 13)) } output: 55 55 55
函数格式 func 函数名(无参/参数1, 参数2...)(返回结果|无){ 函数体}
执行发现这三个函数返回结果相同,只是格式不同
add-函数符合基本的函数格式
add2-对于同一类型参数,可以在最后一个参数后面指明参数类型
add3-函数返回结果可以在返回结构中声明
- 多值返回
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) } output: world hello
函数可以返回任意数量的返回值。
swap 函数返回了两个字符串。
变量
package main import "fmt" var ( a bool b string c int //有符号-等于cpu位数-- 如果是64位(-9223372036854775808 到 9223372036854775807)-`uname -m` c1 int8 //有符号-占用8bit(-128 到 127),int16,int32,int64类推 d uint //无符号-等于cpu位数- 如果是64位(0 到 18446744073709551615) d1 uint8 //无符号-占用8bit(0 到 255), uint16,uint32,uint64类推 e rune // int32 别名 f byte // uint8 别名 g float32 //1.401298464324817070923729583289916131280e-45 -- 3.402823466385288598117041834516925440e+38 (23位小数f,8位偏置指数e,1位符号s) h float64 //4.940656458412465441765687928682213723651e-324 -- 1.797693134862315708145274237317043567981e+308(52位小数f,11位偏置指数e,1位符号s) j complex64 //32 位实数和虚数 k complex128 //64 位实数和虚数 ) var aa, bb, ee bool var ff, gg int = 5, 6 var hh, ii = 5, true func main() { var aaa, bbb bool = true, false ccc, ddd := true, 18 fmt.Println(a, b, c, c1, d, d1, e, f, g, h, j, k) fmt.Println(aa, bb, ee) fmt.Println(ff, gg) fmt.Println(hh, ii) fmt.Println(aaa, bbb) fmt.Println(ccc, ddd) } output: false 0 0 0 0 0 0 0 0 (0+0i) (0+0i) false false false 5 6 5 true true false true 18
go 基本的数据类型有bool类型,字符串类型,数字类型(复数类型)
默认值分别为 false, "", 0(0+0i)
变量定义可以包含初始值,每个变量对应一个。
如果省略类型;变量从初始值中获得类型
在函数中, :=
简洁赋值语句在明确类型的地方,可以用于替代 var 定义。
函数外的每个语句都必须以关键字开始( var
、 func
、等等), :=
结构不能使用在函数外
- 数字类型转换
package main import ( "fmt" "math" "strconv" ) //StringToInt 字符串转整形 func StringToInt(valstr string) int { val, err := strconv.Atoi(valstr) if err != nil { val = 0 } return val } //IntToString 整形转字符串 func IntToString(intval int) string { return strconv.Itoa(intval) } func main() { var x, y int = 3, 4 var f float64 = math.Sqrt(float64(x*x + y*y)) var z int = int(f) fmt.Println(x, y, z) a := IntToString(x) + "string" b := "168" c := StringToInt(b) fmt.Println(a, b+"s", c) } output: 3 4 5 3string 168s 168
表达式 T(v) 将值 v 转换为类型 T
数字类型一般可以直接显式转化,字符串和数字类型可以借助strconv包处理
- 类型推导
package main import "fmt" func main() { i := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128 var j bool k := j fmt.Printf("The types is %T, %T, %T, %T\n", i, f, g, k) } output: The types is int, float64, complex128, bool
在定义一个变量但不指定其类型时(使用没有类型的 var 或 := 语句), 变量的类型由右值推导得出。当右值定义了类型时,新变量的类型与其相同
常量
package main import "fmt" const Pi = 3.14 const ( Big = 1 << 100 Small = Big >> 99 ) func needInt(x int) int { return x*10 + 1 } func needFloat(x float64) float64 { return x * 0.1 } func main() { const World = "世界" fmt.Println("Hello", World) fmt.Println("Happy", Pi, "Day") const Truth = true fmt.Println("Go rules?", Truth) fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) } output: Hello 世界 Happy 3.14 Day Go rules? true 21 0.2 1.2676506002282295e+29
常量的定义与变量类似,只不过使用 const 关键字。
常量可以是字符、字符串、布尔或数字类型的值。
常量不能使用 := 语法定义。
一个未指定类型的数值常量可以作为不同数字类型传参
循环
package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) sum = 1 for sum < 1000 { sum += sum } fmt.Println(sum) } output: 45 1024
Go 只有一种循环结构—— for
循环。
基本的 for 循环除了没有了 ( )
之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中做的一样,而 { }
是必须的。
- 死循环
package main import "fmt" func main() { for { fmt.Println("hello world") } } output: hello world hello world ....
不手动停止的话,代码会一直运行下去
if 语句
package main import ( "fmt" "math" ) func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) } output: 1.4142135623730951 2i
if 语句除了没有了 ( )
之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中的一样,而 { }
是必须的。
- if 的便捷语句
package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } func pow2(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // 这里开始就不能使用 v 了 return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), pow2(3, 2, 10), pow2(3, 3, 20), ) } output: 27 >= 20 9 20 9 20
跟 for 一样, if
语句可以在条件之前执行一个简单的语句。
由这个语句定义的变量的作用域仅在 if 范围之内。
在 if 的便捷语句定义的变量同样可以在任何对应的 else 块中使用。
switch 语句
package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } } output: Go runs on OS X
switch 结构同其他语言一样
switch 的条件从上到下的执行,当匹配成功的时候停止。
每条case除非以 fallthrough 语句结束,否则分支会自动终止。
- 没有条件的 switch
package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } } output: Good evening.
没有条件的 switch 同 switch true
一样。
这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。
defer 语句
package main import "fmt" func echo(str string) string { defer fmt.Println("echo end") return "hello " + str } func main() { defer fmt.Println("world") fmt.Println("hello") echo("china") } output: hello echo end world
defer 语句会延迟函数的执行直到上层函数返回。
延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用
- defer 栈
package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 3; i++ { defer fmt.Println(i) } fmt.Println("done") } output: counting done 2 1 0
延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。
派生类型
指针
package main import "fmt" func main() { i, j := 42, 2701 p := &i // point to i fmt.Println(*p) // read i through the pointer *p = 21 // set i through the pointer fmt.Println(i) // see the new value of i p = &j // point to j *p = *p / 37 // divide j through the pointer fmt.Println(j) // see the new value of j } output: 42 21 73
Go 具有指针。 指针保存了变量的内存地址。
类型 *T 是指向类型 T 的值的指针。其零值是 nil
。
int
& 符号会生成一个指向其作用对象的指针。
i := 42
p = &i
* 符号表示指针指向的底层的值。
p) // 通过指针 p 读取 i*p = 21 // 通过指针 p 设置 i
这也就是通常所说的“间接引用”或“非直接引用”。
结构体
package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} fmt.Println(v) v.X = 4 fmt.Println(v) p := &v p.X = 1.5e9 fmt.Println(v, v.X) fmt.Println(p, p.X) fmt.Println("------------------------") v1 := Vertex{1, 2} // 类型为 Vertex v2 := Vertex{X: 1} // Y:0 被省略 v3 := Vertex{} // X:0 和 Y:0 p1 := &Vertex{1, 2} // 类型为 *Vertex fmt.Println(v1, v2, v3, p1) fmt.Printf("values is %+v, %+v, %+v, %+v \n", v1, v2, v3, p1) fmt.Printf("types is %T, %T, %T, %T \n", v1, v2, v3, p1) fmt.Printf("values is %#v, %#v, %#v, %#v \n", v1, v2, v3, p1) } output: {1 2} {4 2} {1500000000 2} 1500000000 &{1500000000 2} 1500000000 ------------------------ {1 2} {1 0} {0 0} &{1 2} values is {X:1 Y:2}, {X:1 Y:0}, {X:0 Y:0}, &{X:1 Y:2} types is main.Vertex, main.Vertex, main.Vertex, *main.Vertex values is main.Vertex{X:1, Y:2}, main.Vertex{X:1, Y:0}, main.Vertex{X:0, Y:0}, &main.Vertex{X:1, Y:2}
一个结构体( struct
)就是一个字段的集合。
结构体字段使用点号来访问.
结构体字段可以通过结构体指针来访问。
数组
package main import "fmt" func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) }
类型 [n]T 是一个有 n 个类型为 T 的值的数组。
表达式 var a [10]int
定义变量 a 是一个有十个整数的数组。
数组的长度是其类型的一部分,因此数组不能改变大小
这看起来是一个制约,但是请不要担心; Go 提供了更加便利的方式来使用数组。
slice
package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) for i := 0; i < len(p); i++ { fmt.Printf("p[%d] == %d\n", i, p[i]) } } output: p == [2 3 5 7 11 13] p[0] == 2 p[1] == 3 p[2] == 5 p[3] == 7 p[4] == 11 p[5] == 13
一个 slice 会指向一个序列的值,并且包含了长度信息。
[]T
是一个元素类型为 T
的 slice。
- 对 slice 切片
package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) fmt.Println("p[1:4] ==", p[1:4]) // 省略下标代表从 0 开始 fmt.Println("p[:3] ==", p[:3]) // 省略上标代表到 len(s) 结束 fmt.Println("p[4:] ==", p[4:]) }
slice 可以重新切片,创建一个新的 slice 值指向相同的数组。
表达式 s[lo:hi]
表示从 lo 到 hi-1 的 slice 元素,含两端。
因此 s[lo:lo]
是空的,而 s[lo:lo+1]
有一个元素。
- 构造 slice
package main import "fmt" func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) } output: a len=5 cap=5 [0 0 0 0 0] b len=0 cap=5 [] c len=2 cap=5 [0 0] d len=3 cap=3 [0 0 0]
slice 由函数 make 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组:
a := make([]int, 5) // len(a)=5
为了指定容量,可传递第三个参数到 make
:
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
- nil slice
package main import "fmt" func main() { var z []int fmt.Println(z, len(z), cap(z)) if z == nil { fmt.Println("nil!") } } output: [] 0 0 nil!
slice 的零值是 nil
。
一个 nil 的 slice 的长度和容量是 0。
- 向 slice 添加元素
package main import "fmt" func main() { var a []int printSlice("a", a) // append works on nil slices. a = append(a, 0) printSlice("a", a) // the slice grows as needed. a = append(a, 1) printSlice("a", a) // we can add more than one element at a time. a = append(a, 2, 3, 4) printSlice("a", a) } func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) } output: a len=0 cap=0 [] a len=1 cap=1 [0] a len=2 cap=2 [0 1] a len=5 cap=6 [0 1 2 3 4]
向 slice 添加元素是一种常见的操作,因此 Go 提供了一个内建函数 append
。 内建函数的 文档
对 append
有详细介绍。
func append(s []T, vs ...T) []T
append
的第一个参数 s
是一个类型为 T
的数组,其余类型为 T
的值将会添加到 slice。
append
的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。
如果 s
的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。
返回的 slice 会指向这个新分配的数组。
map
package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) m := make(map[string]int) m["Answer"] = 42 fmt.Println("The value:", m["Answer"]) m["Answer"] = 48 fmt.Println("The value:", m["Answer"]) delete(m, "Answer") fmt.Println("The value:", m["Answer"]) v, ok := m["Answer"] fmt.Println("The value:", v, "Present?", ok) } output: {40.68433 -74.39967} The value: 42 The value: 48 The value: 0 The value: 0 Present? false
map 映射键到值。
map 在使用之前必须用 make 而不是 new 来创建;值为 nil 的 map 是空的,并且不能赋值
在 map m 中插入或修改一个元素:
m[key] = elem
获得元素:
elem = m[key]
删除元素:
delete(m, key)
通过双赋值检测某个键存在:
elem, ok = m[key]
如果 key 在 m 中, ok
为 true 。否则, ok 为 false
,并且 elem 是 map 的元素类型的零值。
同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。
range
package main import ( "fmt" "sort" ) var pow = []int{1, 2, 4, 8} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } m := make(map[string]int) m["Answer"] = 42 m["welcome"] = 48 var mp []string for k, v := range m { fmt.Printf("key:%s, value:%d \n", k, v) mp = append(mp, k) } sort.Strings(mp) for _, k := range mp { fmt.Printf("key:%s, value:%d \n", k, m[k]) } pow := make([]int, 3) for i := range pow { pow[i] = 1 << uint(i) } for _, value := range pow { fmt.Printf("%d\n", value) } } output: 2**0 = 1 2**1 = 2 2**2 = 4 2**3 = 8 key:Answer, value:42 key:welcome, value:48 key:Answer, value:42 key:welcome, value:48 1 2 4
for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。
可以通过赋值给 _
来忽略序号和值。
如果只需要索引值,去掉“, value”的部分即可。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- TiDB入门(四):从入门到“跑路”
- MyBatis从入门到精通(一):MyBatis入门
- MyBatis从入门到精通(一):MyBatis入门
- Docker入门(一)用hello world入门docker
- 赵童鞋带你入门PHP(六) ThinkPHP框架入门
- 初学者入门 Golang 的学习型项目,go入门项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。