前言

本来是打算学习C++的,用来开发web服务器后端,但是在学习过程中,发现对于新手有点难,内存管理什么的,巴拉巴拉,然后目标盯上了Rust语言,正准备学习的时候,群友推荐我尝试尝试Go语言,在尝试了Go之后确实被吸引了,打算了解了解,在此记录一下自己的学习之路。

HelloWorld

任何语言的第一个程序都应该是hello world(应该);

Go语言的hello world如下:

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
fmt.Println("Hello World!")
// fmt.Printf("Hello World!\n")
}

执行代码后的输出结果:

基础语法

接下来对这段代码进行一个剖析;

首先第一行 package main ,Go程序是通过包也就是package来组织的,这段代码说明当前的go文件是属于哪个包,在这个例子中包名为 main ,也就意味着这是一个独立的包,在执行编译之后会生成一个可执行文件;

如果要在该主程序中引入其他的go文件,也就是其他的包,那么只有该 main 主包会生成可执行文件,其他的引入的go文件编译之后只会产生 *.a 文件(也就是包文件);

需要注意的是,每个Go程序必须包含一个 main 主包(有且只有一个),在这个主包内,且必须含有一个 main 主函数(有且只有一个)入口,该主函数无参数,无返回值。

第二行 import "fmt" ,和其他编译类语言一样,对于C++或者C语言来说,需要 #include <libName> ,对于Go程序来说,也是需要引入“库”(Go语言中叫“包”),很多的标准函数都是可以通过引入标准包(也就是系统包)来进行调用的。示例这个程序需要进行打印信息,需要调用 Println() 函数,所以需要引入系统包 fmt

第三行 func main() { } ,这一行是通过关键字 func 定义了一个 main() 函数,函数体(也就是函数的具体内容)被放在了 { } 大括号中。

第四行 fmt.Println("Hello World!") ,调用了 fmt 包中定义的函数 Println() ,需要输出的内容被引号包裹;对于第五行的代码,它和本行的代码时相似的,Println() 函数代替了 Printf("\n") 可以直接在信息被打印之后进行自动换行,不需要手动去敲换行符,这两个函数可以根据需要去选择调用。

可以看到,Go程序的函数调用,是通过 <packageName>.<functionName> 的方式进行调用的,类似于C++语言中的 namespace 命名空间,在某些情况下可以不声明package名的,但是推荐进行声明,避免函数同名冲突。

第五行,在Go程序内部的单行注释,是以 //开头的,多行注释的结构是 /* */ 这样的。同时可以看到,输出的内容里面包含了很多非 ASCII 码字符,Go程序是天生支持UTF-8的,任何字符都可以直接输出,甚至可以用UTF-8中的任何字符作为标识符。

需要非常注意的是,在Go语言中一行代表一个语句的结束,并不需要类似于 ; 这样的分号来分行,Go语言编辑器会自动完成;当然也不是强制性的要求,可以将多行写在一起,用分号隔开:

1
2
3
4
5
fmt.Println("Hello!")
fmt.Println("World!")
// 等同于:
fmt.Println("Hello!"); fmt.Println("World!")
//但实际开发中是非常不推荐多个语句写在一行中的

所以在上述HelloWorld的例子中,func main() 是不能和 { 在不同行中的,这必将会引起错误,示例如下:

1
2
3
4
5
6
7
8
func main() {
// 这是正确的写法
}

func main()
{
// 这是错误的写法
}

这一点非常要注意,一不小心就会写错!

标识符用来命名变量、函数等程序实体,例如hello world例子中,mainPrintln 都是标识符。

一个标识符实际上就是一个或是多个字母 A~Za~z ,数字 0~9 以及下划线 _ 组成的序列;

需要注意的是,第一个字符必须是字母或下划线,不能是数字

标识符的命名,非常推荐使用标准驼峰格式,例如 aaaBbbCcc ,不推荐使用大驼峰格式 AaaBbbCcc ,因为在Go语言中,与JavaScript中的 export 语法一样,如果变量名称以大写字母开头,则Go会导出变量,可以在外部被访问到,例如示例中 fmt.Println()Println 就是 fmt 包中的函数,首字母就是大写的,可以通过引入包来让外部程序访问到该包内的函数/变量等。

所有其他不以大写字母开头的变量对程序包来说都是私有的
Go语言中的保留关键字,也就是不能拿来作为变量/函数的命名:

1
2
break  default  func  interface  select  case  defer  go  map  struct  chan  else  goto
package switch const fallthrough if var range type continue for import return

同时还有部分预定义标识符:

1
2
3
append  bool  byte  cap  close  complex  complex64  complex128	uint16  copy  false
float32 float64 imag int int8 int16 uint32 int32 int64 iota len make new nil
panic uint64 print println real recover string true unit uint8 uintptr

对于字符串的简单拼接,可以直接使用 + 即可,例如:

1
fmt.Println("Hello" + "World!")

输出:

格式化字符串:

  • Sprintf() 根据格式化参数生成格式化的字符串并返回该字符串。
  • Printf() 根据格式化参数生成格式化的字符串并输出。

举例:

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

import (
"fmt"
)

func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode = 123
var enddate = "2020-12-31"
var url = "Code = %d & endDate = %s"
var target_url = fmt.Sprintf(url, stockcode, enddate)
fmt.Println(target_url)
}

输出:

stockcode 传入 url 中的 %denddate 传入 url 中的 %s ,最后将新字符串赋值给 target_url ;在go程序中只能通过 Sprintf() 来格式化字符串,再举个例子:

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

import "fmt"

func main() {
var stockcode = 123
var enddate = "2020-12-31"
fmt.Printf("Code = %d & endDate = %s", stockcode, enddate) // 或者将本语句替换为下面这种写法
// 也可以如下这种写法
// var url="Code = %d & endDate = %s"
// fmt.Printf(url,stockcode,enddate)
}

输出:

这是 Printf() 的用法,且只能用 Printf() ,如果用 Println() 的话是如下的输出:

可以看到字符串并没有被正确的加载。

定义变量

对于Go语言,定义变量使用的是常见的关键字 var ,但和C++不同的是,Go语言会将变量类型放在变量名后,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 定义了一个名字叫number,类型为int的变量
var number int

// 定义了三个名字不同但类型为int的变量
// 注意逗号和空格的使用
var num1, num2, num3 int

// 定义一个名字叫number,类型为int,初始值为1的变量
var number int = 1

// 定义三个有初始值的相同类型变量
// 注意逗号和空格的使用
var num1, num2, num3 int = 1, 2, 3

当然,这是标准的变量定义和赋值方式,go语言里为了方便可以进行简化,例如:

1
2
3
4
5
6
var num1, num2, num3 int = 1, 2, 3
// 可以简化为:
var num1, num2, num3 = 1, 2, 3
// go语言中可以自动识别变量类型对变量进行声明,根据等号右边的表达式自动推导出等号左边变量的类型,完成变量定义和声明
// 可以继续简化为:
num1, num2, num3 := 1, 2, 3

在最终的变量定义语句中,可以看到 := 代替了 varint 的作用,可以直接对变量进行定义,但是该种方法只能用在函数内部,在函数外部还是需要使用 var 关键字。
特殊的变量名:下划线 _ ,当下划线作为一个变量名的时候,表示对其赋的值进行丢弃,相当于说是个垃圾场,但不可回收,举个例子,现在有一串数字 1, 2, 3 我只需要收到 23 ,那么可以这样写:

1
_, num1, num2 = 1, 2, 3

1 的值赋给了下划线,表示被丢弃,2 的值赋给了 num13 的值赋给了 num2
Go语言中的变量,就和喜欢的姑娘一样,请务必负责到底,如果你和她在一起了(定义了这个变量),却没有负责到底(使用这个变量),那么就会挨打(编译器会报错)!例如:

1
2
3
4
5
func main() {
x := 1
// fmt.Println(x)
fmt.Println("Hello!")
}

输出:

注意,在上述例子中,我们为什么要多此一举去写一句 fmt.Println("Hello!") ,因为和变量一样,如果我们引用了 fmt 这个包,但是没有去使用这个包,编译器同样也会报错!

鸽子

临时被安排了其他开发任务,要开始主攻 C++/CAPE-OPEN 方向了,Go语言的学习先暂告一段落~