Mark blog

知行合一 划水归档

Go 语言简介(一)

为了解决在21世纪多核和网络化环境下越来越复杂的编程问题而发明了Go语言,编程语言的演化就像生物物种的演化一样,一个成功的编程语言的后代都会继承他们祖先的优点,当然有时多种语言杂合也会产生令人惊讶的特性,还有一些激进的新特性可能并没有先例,我们可以通过观察编程语言和软硬件环境是如何相互促进,相互影响的演化过程而学到很多.

Go语言有时候被描述成”C类似语言”或者”21世纪的C语言”.Go从C语言继承了相似的表达语法,流程控制,基础数据类型,调用参数传值,指针等很多思想,还有C语言一直所看重的编译后机器码的运行效率以及和现有操作系统的无缝适配.

开始一个 Hello World

文件名后缀 hello.go

1
2
3
4
5
6
7
package main //声明本文件的package名

import "fmt" //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/目录。

使用相对路径:

1
import` `"./haoel"`  `//import当前目录里haoel子目录里的所有的go文件

使用GOPATH路径

1
`import` `"./haoel"`  `//import当前目录里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型

还有一种定义变量的方式

1
x := 100 //等价于 var x int = 100;

常量很简单,使用const关键字:

1
2
const s string = "hello world"
const pi float32 = 3.1415926

数组

直接看代码

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不同的是,这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号,因此输入源代码多数时候就不需要分号了

规则是这样的:如果在一个新行前方的最后一个标记是一个标识符(包括像intfloat64这样的单词)、一个基本的如数值这样的文字、或以下标记中的一个时,会自动插入分号:

1
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。他们所做的事不同,所应用到的类型也不同,这可能引起混淆,但规则却很简单。

参考自:

GO 语言简介(上)— 语法

https://coolshell.cn/articles/8460.html