这是我在2018年低记得笔记,放在这里记录一下。
¶ GO语言的安装
今天学习GO语言的动力是想参与到https://github.com/fatedier/frp 这个项目中来
参考资料:
- 安装包下载:http://www.golangtc.com/download
- GO的安装:http://docscn.studygolang.com/doc/install
- 搭建Go开发及调试环境(LiteIDE + GoClipse) – Windows篇:http://www.cnblogs.com/custa/p/3913526.html
- GO语言指南:https://tour.go-zh.org/list
- GO语言文档:http://docscn.studygolang.com/doc/
- GO语言教程:http://www.runoob.com/w3cnote/wx-xcx-repo.html
¶ 安装描述: Windows 系统下安装
Windows 下可以使用 .msi后缀(在下载列表中可以找到该文件,如:go1.7.3.windows-amd64.msi)的安装包来安装。
默认情况下.msi文件会安装在 c:\Go 目录下。你可以将 c:\Go\bin 目录添加到 PATH 环境变量中。添加后你需要重启命令窗口才能生效
设置GOROOT变量与GO安装路径一致。
¶ 安装测试
- 创建工作目录 C:\>Go_WorkSpace。
- 文件名: test.go,代码如下:
1 | package main |
- 使用 go 命令执行以上代码输出结果如下:
1 | C:\Go_WorkSpace>go run test.go |
¶ GO的语言结构
Go 语言最简单程序的结构
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
下面来看一段程序代码:
1 | //第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。 |
¶ 执行 Go 程序
- 打开编辑器如Sublime2,将以上代码添加到编辑器中。
- 将以上代码保存为 hello.go
- 打开命令行,并进入程序文件保存的目录中。
- 输入命令 go run hello.go 并按回车执行代码。
- 如果操作正确你将在屏幕上看到 “Hello World!” 字样的输出。
¶ GO语言的基础语法
¶ Go 标记
Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。如以下 GO 语句由 6 个标记组成:
1 | fmt.Println("Hello, World!") |
6个标记是:
1 | 1. fmt |
¶ 行分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。
以下为两个语句:
1 | fmt.Println("Hello, World!") |
¶ 注释
注释不会被编译,每一个包应该有相关注释。
单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。如:
1 | // 单行注释 |
¶ 标识符
标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(AZ和az)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
以下是有效的标识符:
1 | mahesh kumar abc move_name a_123 |
以下是无效的标识符:
- 1ab(以数字开头)
- case(Go 语言的关键字)
- a+b(运算符是不允许的)
¶ 关键字
下面列举了 Go 代码中会使用到的 25 个关键字或保留字:
名称 | 描述 | 名称 | 描述 | 名称 | 描述 |
---|---|---|---|---|---|
break | 跳出 | default | 默认 | func | 函数 |
interface | 接口 | select | case | 比较 | |
defer | go | map | 键值对 | ||
struct | chan | else | 否则 | ||
goto | 跳转 | package | 包定义 | switch | 开关 |
const | 常量 | fallthrough | if | 如果 | |
range | type | 类型 | continue | 继续 | |
for | O(∩_∩)O | import | 引入 | return | 返回 |
var | 变量 |
除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:
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 |
println | real | recover | string | true | uint | uint8 | uintptr |
程序一般由关键字、常量、变量、运算符、类型和函数组成。
程序中可能会使用到这些分隔符:括号 (),中括号 [] 和大括号 {}。
程序中可能会使用到这些标点符号:.、,、;、: 和 …。
¶ Go 语言的空格
Go 语言中变量的声明必须使用空格隔开,如:
1 | var age int; |
语句中适当使用空格能让程序看易阅读。
无空格:
1 | fruit=apples+oranges; |
在变量与运算符间加入空格,程序看起来更加美观,如:
1 | fruit = apples + oranges; |
¶ Go 语言数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别有以下几种数据类型:
¶ 布尔类型 bool
布尔型的值只可以是常量 true 或者 false。一个简单的例子:
1 | var b bool = true |
¶ 数字类型
整型 int 和浮点型 float,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
¶ 整型:
¶ uint8
无符号 8 位整型 (0 到 255)
¶ uint16
无符号 16 位整型 (0 到 65535)
¶ uint32
无符号 32 位整型 (0 到 4294967295)
¶ uint64
无符号 64 位整型 (0 到 18446744073709551615)
¶ int8
有符号 8 位整型 (-128 到 127)
¶ int16
有符号 16 位整型 (-32768 到 32767)
¶ int32
有符号 32 位整型 (-2147483648 到 2147483647)
¶ int64
有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
¶ 浮点型:
¶ float32
IEEE-754 32位浮点型数
¶ float64
IEEE-754 64位浮点型数
¶ complex64
32 位实数和虚数
¶ complex128
64 位实数和虚数
¶ 其他数字类型:
¶ byte
类似 uint8
¶ rune
类似 int32
¶ uint
32 或 64 位
¶ int
与 uint 一样大小
¶ uintptr
无符号整型,用于存放一个指针
¶ GO语言变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。
Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。
声明变量的一般形式是使用 var 关键字:
1 | var identifier type |
¶ 变量声明
- 指定变量类型,声明后没有赋值则使用默认值;
1 | var v_name v_type |
- 根据值自行判断变量类型;也就是弱类型声明。
1 | var v_name = value |
- 省略var,注意:=左侧的变量不应该是已经声明过的,否则会导致编译错误。
1 | v_name := value |
示例如下:
1 | package main |
运行结果:
1 | arlin 是个好人 arlin.wang false |
注意:由于有中文,采用记事本编辑默认是ANSI码上一段代码报错
1 | # command-line-arguments |
我的解决办法是用notpadd++编辑,报错格式为UTF-8
¶ 多变量声明
1 | //类型相同多个变量, 非全局变量 |
实例如下:
1 | package main |
运行结果:
1 | 0 0 0 false 1 2 123 arlin 123 arlin |
注意:var ( a int b bool) 是() 而不是{}
¶ 值类型和引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
1 | graph LR |
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
1 | graph LR |
1 | graph LR |
你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。
内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
1 | graph LR |
这个内存地址为称之为指针,这个指针实际上也被存在另外的某一个字中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
¶ 简短形式,使用:=赋值操作符
我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。
a 和 b 的类型(int 和 bool)将由编译器自动推断。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
¶ 注意事项
如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:
1 | func main() { |
尝试编译这段代码将得到错误 a declared and not used。
此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用:
1 | func main() { |
但是全局变量是允许声明后不使用的。
同一类型的多个变量可以在同一行声明和赋值如:
1 | var a , b, c int = 1,2,3 |
多变量可以在同一行进行赋值,如:
1 | b ,c ,d = 5,7,"abc" |
上面这行假设了变量 b,c 和 d 都已经被声明,否则的话应该这样使用:
1 | b, c ,d:= 5, 7, "abc" |
这被称为 并行 或 同时 赋值。
如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a。
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1)。
¶ Go语言常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
1 | const identifier [type] = value |
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。
- 显式类型定义: const b string = “abc”
- 隐式类型定义: const b = “abc”
多个相同类型的声明可以简写为:
1 | const c_name1, c_name2 = value1, value2 |
示例:
1 | package main |
运行结果为:
1 | 面积为 : 50 |
常量还可以用作枚举:
1 | //数字 0、1 和 2 分别代表未知性别、女性和男性。 |
常量可以用len(), cap(), unsafe.Sizeof()常量计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:
1 | package main |
运行结果为:
1 | abc 3 16 |
unsafe 库让 golang 可以像 C 语言一样操作计算机内存,但这并不是 golang 推荐使用的,能不用尽量不用,就像它的名字所表达的一样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。
¶ iota 常量
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
iota 可以被用作枚举值:
1 | const ( |
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
1 | const ( |
¶ iota用法
1 | package main |
运行结果:
1 | 0 1 2 ha ha 100 100 7 8 |
实例二:
1 | package main |
运行结果为:
1 | i= 1 |
iota表示从0开始自动加1,所以i=1<<0,j=3<<1(<<表示左移的意思),即:i=1,j=6,这没问题,关键在k和l,从输出结果看,k=3<<2,l=3<< 3。
¶ GO语言运算符
运算符用于在程序运行时执行数学或逻辑运算,有:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
¶ 算术运算符
下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。
运算符号 | 描述 | 实例 |
---|---|---|
+ | 相加 | A + B 输出结果 30 |
- | 相减 | A - B 输出结果 -10 |
* | 相乘 | A * B 输出结果 200 |
/ | 相除 | B / A 输出结果 2 |
% | 求余 | B % A 输出结果 0 |
++ | 自增 | A++ 输出结果 11 |
– | 自减 | A-- 输出结果 9 |
以下实例演示了各个算术运算符的用法:
1 | package main |
运行结果:
1 | 第一行 - c 的值为 31 |
¶ 关系运算符
下表列出了所有Go语言的关系运算符。假定 A 值为 10,B 值为 20。
运算符号 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 | (A == B) 为 False |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 | (A != B) 为 True |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 | (A > B) 为 False |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 | (A < B) 为 True |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 | (A >= B) 为 False |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 | (A <= B) 为 True |
以下实例演示了关系运算符的用法:
1 | package main |
运行结果:
1 | 第一行 - a 不等于 b |
¶ 逻辑运算符
下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符号 | 描述 | 实例 |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A |
| | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
以下实例演示了逻辑运算符的用法:
1 | package main |
以上实例运行结果:
1 | 第二行 - 条件为 true |
¶ 位运算符
位运算符对整数在内存中的二进制位进行操作。
下表列出了位运算符 &, |, 和 ^ 的计算:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假定 A = 60; B = 13; 其二进制数转换为:
1 | A = 0011 1100 |
C 语言支持的位运算符如下表所示。假定 A 为60,B 为13:
运算符号 | 描述 | 实例 |
---|---|---|
& | 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A | B) 结果为 61, 二进制为 0011 1101 |
^ | 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
<< | 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 | A << 2 结果为 240 ,二进制为 1111 0000 其功能把"<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。 |
>> | 右移运算符">>“是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
以下实例演示了逻辑运算符的用法:
1 | package main |
运行结果:
1 | 第一行 - c 的值为 12 |