Go 语言简介(上)— 语法
2015-12-19 13:16 GarfieldEr007 阅读(215) 评论(0) 编辑 收藏 举报周末天气不好,只能宅在家里,于是就顺便看了一下Go语言,觉得比较有意思,所以写篇文章介绍一下。我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章。所以,下面的文章主要是以代码和注释为主。只需要你对C语言,Unix,Python有一点基础,我相信你会在30分钟左右读完并对Go语言有一些初步了解的。
Hello World
1
2
3
4
5
6
7
|
package main //声明本文件的package名 import "fmt" //import语言的fmt库——用于输出 func main() { fmt.Println( "hello world" ) } |
运行
你可以有两种运行方式,
1
2
|
$go run hello.go hello world |
1
2
3
4
5
6
7
|
$go build hello.go $ ls hello hello.go $. /hello hello world |
自己的package
你可以使用GOPATH环境变量,或是使用相对路径来import你自己的package。
Go的规约是这样的:
1)在import中,你可以使用相对路径,如 ./或 ../ 来引用你的package
2)如果没有使用相对路径,那么,go会去找$GOPATH/src/目录。
1
|
import "./haoel" //import当前目录里haoel子目录里的所有的go文件 |
1
|
import "haoel" //import 环境变量 $GOPATH/src/haoel子目录里的所有的go文件 |
fmt输出格式
fmt包和libc里的那堆使用printf, scanf,fprintf,fscanf 很相似。下面的东西对于C程序员不会陌生。
注意:Println不支持,Printf才支持%式的输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main import "fmt" import "math" func main() { fmt.Println( "hello world" ) fmt.Printf( "%t\n" , 1 == 2 ) fmt.Printf( "二进制:%b\n" , 255 ) fmt.Printf( "八进制:%o\n" , 255 ) fmt.Printf( "十六进制:%X\n" , 255 ) fmt.Printf( "十进制:%d\n" , 255 ) fmt.Printf( "浮点数:%f\n" , math.Pi) fmt.Printf( "字符串:%s\n" , "hello world" ) } |
当然,也可以使用如\n\t\r这样的和C语言一样的控制字符
变量和常量
变量的声明很像 javascript,使用 var关键字。注意:go是静态类型的语言,下面是代码:
1
2
3
4
5
6
7
8
|
//声明初始化一个变量 var x int = 100 var str string = "hello world" </pre> //声明初始化多个变量 var i, j, k int = 1, 2, 3 //不用指明类型,通过初始化值来推导 var b = true //bool型 |
还有一种定义变量的方式(这让我想到了Pascal语言,但完全不一样)
1
|
x := 100 //等价于 var x int = 100; |
常量很简单,使用const关键字:
1
2
|
const s string = "hello world" const pi float32 = 3.1415926 |
数组
直接看代码(注意其中的for语句,和C很相似吧,就是没有括号了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func main() { var a [ 5 ] int fmt.Println( "array a:" , a) a[ 1 ] = 10 a[ 3 ] = 30 fmt.Println( "assign:" , a) fmt.Println( "len:" , len(a)) b := [ 5 ] int { 1 , 2 , 3 , 4 , 5 } fmt.Println( "init:" , b) var c [ 2 ][ 3 ] int for i := 0 ; i < 2 ; i++ { for j := 0 ; j < 3 ; j++ { c[i][j] = i + j } } fmt.Println( "2d: " , c) } |
运行结果:
1
2
3
4
5
|
array a: [0 0 0 0 0] assign: [0 10 0 30 0] len: 5 init: [1 2 3 4 5] 2d: [[0 1 2] [1 2 3]] |
数组的切片操作
这个很Python了。
1
2
3
4
5
6
7
8
9
10
|
a := [ 5 ] int { 1 , 2 , 3 , 4 , 5 } b := a[ 2 : 4 ] // a[2] 和 a[3],但不包括a[4] fmt.Println(b) b = a[: 4 ] // 从 a[0]到a[4],但不包括a[4] fmt.Println(b) b = a[ 2 :] // 从 a[2]到a[4],且包括a[2] fmt.Println(b) |
分支循环语句
if语句
注意:if 语句没有圆括号,而必需要有花括号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//if 语句 if x % 2 == 0 { //... } //if - else if x % 2 == 0 { //偶数... } else { //奇数... } //多分支 if num < 0 { //负数 } else if num == 0 { //零 } else { //正数 } |
switch 语句
注意:switch语句没有break,还可以使用逗号case多个值
1
2
3
4
5
6
7
8
9
10
11
12
|
switch i { case 1: fmt.Println( "one" ) case 2: fmt.Println( "two" ) case 3: fmt.Println( "three" ) case 4,5,6: fmt.Println( "four, five, six" ) default : fmt.Println( "invalid value!" ) } |
for 语句
前面你已见过了,下面再来看看for的三种形式:(注意:Go语言中没有while)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//经典的for语句 init; condition; post for i := 0; i<10; i++{ fmt.Println(i) } //精简的for语句 condition i := 1 for i<10 { fmt.Println(i) i++ } //死循环的for语句 相当于for(;;) i :=1 for { if i>10 { break } i++ } |
关于分号
从上面的代码我们可以看到代码里没有分号。其实,和C一样,Go的正式的语法使用分号来终止语句。和C不同的是,这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号,因此输入源代码多数时候就不需要分号了。
规则是这样的:如果在一个新行前方的最后一个标记是一个标识符(包括像int
和float64
这样的单词)、一个基本的如数值这样的文字、或以下标记中的一个时,会自动插入分号:
break continue fallthrough return ++ -- ) }
通常Go程序仅在for
循环语句中使用分号,以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句,也需要用分号分开。
注意:无论任何时候,你都不应该将一个控制结构((if
、for
、switch
或select
)的左大括号放在下一行。如果这样做,将会在大括号的前方插入一个分号,这可能导致出现不想要的结果。
map
map在别的语言里可能叫哈希表或叫dict,下面是和map的相关操作的代码,代码很容易懂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
func main(){ m := make(map[string] int ) //使用make创建一个空的map m[ "one" ] = 1 m[ "two" ] = 2 m[ "three" ] = 3 fmt.Println(m) //输出 map[three:3 two:2 one:1] (顺序在运行时可能不一样) fmt.Println(len(m)) //输出 3 v := m[ "two" ] //从map里取值 fmt.Println(v) // 输出 2 delete (m, "two" ) fmt.Println(m) //输出 map[three:3 one:1] m1 := map[string] int { "one" : 1, "two" : 2, "three" : 3} fmt.Println(m1) //输出 map[two:2 three:3 one:1] (顺序在运行时可能不一样) for key, val := range m1{ fmt.Printf( "%s => %d \n" , key, val) /*输出:(顺序在运行时可能不一样) three => 3 one => 1 two => 2*/ } } |
指针
Go语言一样有指针,看代码
1
2
3
4
5
6
7
8
9
10
11
12
|
var i int = 1 var pInt * int = &i //输出:i=1 pInt=0xf8400371b0 *pInt=1 fmt.Printf( "i=%d\tpInt=%p\t*pInt=%d\n" , i, pInt, *pInt) *pInt = 2 //输出:i=2 pInt=0xf8400371b0 *pInt=2 fmt.Printf( "i=%d\tpInt=%p\t*pInt=%d\n" , i, pInt, *pInt) i = 3 //输出:i=3 pInt=0xf8400371b0 *pInt=3 fmt.Printf( "i=%d\tpInt=%p\t*pInt=%d\n" , i, pInt, *pInt) |
Go具有两个分配内存的机制,分别是内建的函数new和make。他们所做的事不同,所应用到的类型也不同,这可能引起混淆,但规则却很简单。
内存分配
new 是一个分配内存的内建函数,但不同于其他语言中同名的new所作的工作,它只是将内存清零,而不是初始化内存。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址,也就是一个类型为*T的值。用Go的术语来说,就是它返回了一个指向新分配的类型为T的零值的指针。
make(T,
args)
函数的目的与new(T)
不同。它仅用于创建切片、map和chan(消息管道),并返回类型T
(不是*T
)的一个被初始化了的(不是零)实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如,切片是一个具有三项内容的描述符,包括指向数据(在一个数组内部)的指针、长度以及容量,在这三项内容被初始化之前,切片值为nil
。对于切片、映射和信道,make
初始化了其内部的数据结构并准备了将要使用的值。如:
下面的代码分配了一个整型数组,长度为10,容量为100,并返回前10个数组的切片
1
|
make([] int , 10, 100) |
以下示例说明了new
和make
的不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var p *[]int = new ([]int) // 为切片结构分配内存;*p == nil;很少使用 var v []int = make([]int, 10) // 切片v现在是对一个新的有10个整数的数组的引用 // 不必要地使问题复杂化: var p *[]int = new ([]int) fmt.Println(p) //输出:&[] *p = make([]int, 10, 10) fmt.Println(p) //输出:&[0 0 0 0 0 0 0 0 0 0] fmt.Println((*p)[2]) //输出: 0 // 习惯用法: v := make([]int, 10) fmt.Println(v) //输出:[0 0 0 0 0 0 0 0 0 0] |
函数
老实说,我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满(保持和C一样的不可以吗? 呵呵)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main import "fmt" func max(a int , b int ) int { //注意参数和返回值是怎么声明的 if a > b { return a } return b } func main(){ fmt.Println(max( 4 , 5 )) } |
函数返回多个值
Go中很多Package 都会返回两个值,一个是正常值,一个是错误,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package main import "fmt" func main(){ v, e := multi_ret( "one" ) fmt.Println(v,e) //输出 1 true v, e = multi_ret( "four" ) fmt.Println(v,e) //输出 0 false //通常的用法(注意分号后有e) if v, e = multi_ret( "four" ); e { // 正常返回 } else { // 出错返回 } } func multi_ret(key string) ( int , bool){ m := map[string] int { "one" : 1 , "two" : 2 , "three" : 3 } var err bool var val int val, err = m[key] return val, err } |
函数不定参数
例子很清楚了,我就不多说了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func sum(nums ... int ) { fmt.Print(nums, " " ) //输出如 [1, 2, 3] 之类的数组 total := 0 for _, num := range nums { //要的是值而不是下标 total += num } fmt.Println(total) } func main() { sum(1, 2) sum(1, 2, 3) //传数组 nums := [] int {1, 2, 3, 4} sum(nums...) } |
函数闭包
nextNum这个函数返回了一个匿名函数,这个匿名函数记住了nextNum中i+j的值,并改变了i,j的值,于是形成了一个闭包的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func nextNum() func() int { i,j := 1,1 return func() int { var tmp = i+j i, j = j, tmp return tmp } } //main函数中是对nextNum的调用,其主要是打出下一个斐波拉契数 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论