Hello World!

从每个语言学习的开始讲起:

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("hello golang")
}

每个Go程序都是由包构成,每个项目都是从main包作为入口的。

导入包

使用import语句导入包,在Hello World程序中,我们导入了fmt

1
import "fmt"

常常的,我们在写程序时都会遇到需要导入多个包的情况,在Golang中,我们有两种形式导入多个包:

第一种是多个import

1
2
import "fmt"
import "math"

我们还可以选择将多个包用括号括起来在一个import中

1
2
3
4
import (
"fmt"
"math"
)

值得注意的一点:import语句只对当前文件有效

导出名

在Java中,我们用publicprivate关键词来标记一个方法是包外是否可用的。

而在Go中,使用了首字母大小写来表示这个,大写字母则代表public的。例如在我们的第一个程序中的Println就是一个以大写字母开头的。

例子

新建一个hello在目录下,并在下面新建一个Sayhello.go文件

1
2
3
4
5
6
7
8
9
10
11
package hello

import "fmt"

func SayHello() {
fmt.Println("Hello golang")
}

func noHello() {
fmt.Println("No Hello")
}

我们将mian.go修改为

1
2
3
4
5
6
7
8
package main

import "golang_basic/hello"

func main() {
hello.SayHello()
hello.noHello()
}

我们对其运行会发现报错了,因为noHello并不是一个可以在包外调用的函数。

函数

如果你在使用一些智能的IDE,在输入func进行补全的时候,你会发现func的模板与我们学过的语言都不同的地方,它在参数列表后面,会有一个可以填入的地方

image-20230118180440834

这是因为go的函数声明与其它的语言稍微有些不同

声明模板

基本函数声明模板如下

1
2
3
4
func 函数名 (参数列表) (返回值列表){
// 函数体
return 返回值列表
}

它相较其它大多数语言,Go多了一个返回值列表

Go在函数声明上还有一个不同的点就是Go的参数是名称在前,类型在后

1
2
3
func add(x int, y int) int {
return x + y
}

由于上面的例子,参数的类型是相同的,所以我们可以将其类型写在一起

1
2
3
func add(x, y int) int {
return x + y
}

多值返回

Go语言支持多个返回值

1
2
3
func swap(x, y string) (string, string) {
return y, x
}

命名返回值

在返回值列表中,我们可以将返回值命名。我们在给返回值赋值后,可以在最后使用一个return就可以返回所有定义的返回值。

1
2
3
4
func add(x, y int) (ans int) {
ans = x + y
return
}

变量

声明、初始化

在前面我们提到go的函数内参数是名称在前,类型在后的,变量的声明也不例外

1
var 变量名 类型 = 表达式

类型与表达式可以省略一个;

  • 类型省略则会根据表达式自动推断类型
  • 表达式省略则会自动初始化该变量为该类型的零值

前面我们提到Go语言支持多值返回,因此一组变量也可以通过接受函数返回值进行初始化

1
var a, b = swap(a, b)

在接受的时候,我们可以通过下划线抛弃我们不需要的值

1
var a, _ = swap(a, b)

声明也可以像import一样,用括号括起来

1
2
3
4
var (
a = 1
b = 2
)

短声明

短声明:=相当于是一个省略类型的声明语句,可以替代var使用

1
变量名 := 表达式

例子

1
a, b := swap(a, b)

符号

赋值

Go语言的赋值与大多数语言(C,Java…)的赋值都类似

相较于C、Java一类,Go语言加入了一个比较“摩登”的赋值,元祖赋值,一个例子就是上马的接受函数的多变量初始化

1
a, b = b, a

这使得交换两个变量的值不再需要一个中间存储值,这极大便利了变量的交换,减少了不少逻辑上思考的步骤

指针

Go语言的指针与C语言中的指针使用的方法大致相同,也都是通过*&来对地址进行操作

1
var 变量名 *类型

举个例子

1
2
3
4
5
var a *int        //指向整型
var b *float32 //指向浮点型

c := 1
d := &c //我们也可以通过短声明来创建

空指针

当一个指针定义后没有分配给任何变量时,他的值为nil

nil 指针也称为空指针

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

数据类型

基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool

string

int int8 int16 int32 int64 //有符号整型
uint uint8 uint16 uint32 uint64 uintptr //无符号整型

byte //类似uint8

rune //类似int32

float32 float64 //单精度浮点 双精度浮点

complex64 complex128 //实数和虚数

类型转换

Go语言不提供类型自动转换,只可以通过T(v)的方式将值v转换为类型T

1
2
3
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

类型别名

类似于define,在编译的时候会全部替换掉

1
type DataTYPE = string

自定义类型

可以基于已有类型定义初新的类型,互相可以强制转换,可以添加类型自己的方法

1
type newtype string

逻辑控制

for

1
2
3
for i := 0; i < 10; i++ {
sum += i
}

相当于是省略了小括号

注意:大括号为必须存在的

在Go语言中,是没有while的,while可以用省略起始条件和后置语句的for替代

1
2
3
4
sum := 1
for sum < 1000 {
sum += sum
}

而无限循环则为

1
2
for {
}

for range

for range类似于for in。在Go语言中,我们可以使用for range遍历数组、切片、字符串、map及通道(channel)

1
2
3
for key, value := range oldMap { //在数组中,key代表下标,value为对应的值
newMap[key] = value
}

以上代码中,key和value都是可以省略的

如果我们想只读取一个参数,我们可以有两种形式

1
2
3
4
//第一种
for key := range oldMap
//第二种
for _, value := range oldMap

if

1
2
3
4
5
6
7
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
//fmt.Sprint()的作用相当于是拼接字符串

Go语言允许在条件表达式之前,执行一个语句

1
2
3
4
5
6
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}

这个v的作用域是在if这个代码块内的

else与else if的用法也很简单

1
2
3
4
5
6
7
8
var a int
if _, _ = fmt.Scan(&a); a > 10 {
fmt.Println("a > 10")
} else if a == 10 {
fmt.Println("a = 10")
} else {
fmt.Println("a < 10")
}

switch

对比 C 的 switch,更新了以下几点:

  • case 现在不一定要是常量,也可以是表达式

  • 每个分支都是默认 break 的,如果你不想跳出,可以以 fallthrough 语句结束

  • 支持多条件匹配

    1
    2
    3
    4
    switch a {
    case 1,2,3,4:
    default:
    }

举个完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"runtime"
)

func main() {
fmt.Print("Go run on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "linux":
fmt.Println("GNU/Linux")
default:
fmt.Printf("%s\n", os)
}
}

image-20230126215725209

defer

defer 语句会将函数推迟到外层函数返回之后执行

推迟调用的函数其参数会立即求值,但会被压入栈中,直到外层函数返回时才会按照后进先出的顺序出栈

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
fmt.Println("counting")

for i := 0; i < 10; i++ {
defer fmt.Println(i)
}

fmt.Println("done")
}

image-20230126221101474