作者:陈皓
只需要你对C语言,Unix,Python有一点基础,我相信你会在30分钟左右读完并对Go语言有一些初步了解的。
Hello World
文件名 hello.go
1
2
3
4
5
6
7
|
package main
import "fmt"
func main() {
fmt.Println( "hello world" )
}
|
运行
你可以有两种运行方式,
解释执行(实际是编译成a.out再执行)
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/目录。
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
|
还有一种定义变量的方式(这让我想到了Pascal语言,但完全不一样)
常量很简单,使用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
22
|
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
6
|
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
11
|
a := [ 5 ] int { 1 , 2 , 3 , 4 , 5 }
b := a[ 2 : 4 ]
fmt.Println(b)
b = a[: 4 ]
fmt.Println(b)
b = 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 x % 2 == 0 {
}
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 i := 0; i<10; i++{
fmt.Println(i)
}
i := 1
for i<10 {
fmt.Println(i)
i++
}
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 )
m[ "one" ] = 1
m[ "two" ] = 2
m[ "three" ] = 3
fmt.Println(m)
fmt.Println(len(m))
v := m[ "two" ]
fmt.Println(v)
delete (m, "two" )
fmt.Println(m)
m1 := map[string] int { "one" : 1, "two" : 2, "three" : 3}
fmt.Println(m1)
for key, val := range m1{
fmt.Printf( "%s => %d \n" , key, val)
}
}
|
指针
Go语言一样有指针,看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var i int = 1
var pInt * int = &i
fmt.Printf( "i=%d\tpInt=%p\t*pInt=%d\n" , i, pInt, *pInt)
*pInt = 2
fmt.Printf( "i=%d\tpInt=%p\t*pInt=%d\n" , i, pInt, *pInt)
i = 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个数组的切片
以下示例说明了new 和make 的不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var p *[]int = new ([]int)
var v []int = make([]int, 10)
var p *[]int = new ([]int)
fmt.Println(p)
*p = make([]int, 10, 10)
fmt.Println(p)
fmt.Println((*p)[2])
v := make([]int, 10)
fmt.Println(v)
|
函数
老实说,我对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)
v, e = multi_ret( "four" )
fmt.Println(v,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, " " )
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
}
}
func main(){
nextNumFunc := nextNum()
for i:=0; i<10; i++ {
fmt.Println(nextNumFunc())
}
}
|
函数的递归
和c基本是一样的
1
2
3
4
5
6
7
8
9
10
|
func fact(n int ) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
fmt.Println(fact(7))
}
|
结构体
Go的结构体和C的基本上一样,不过在初始化时有些不一样,Go支持带名字的初始化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
type Person struct {
name string
age int
email string
}
func main() {
person |
|
请发表评论