简介
-
GO 从C语言中继承了很多 理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指针等等,也保留了和C语言一样的编译执行方式及弱化的指针。
-
引入 包 的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在。
-
垃圾回收机制,内存自动回收,不需开发人员管理
-
天然并发
(1)从语言层面支持并发,实现简单。
(2) goroutine,轻量级线程,可实现大并发处理,高效利用多核。
(3)基于CPS并发模型(Communicating Sequential Processes )实现。 -
吸收了管道通信机制,形成 Go 语言特有的管道 channel ,通过管道,可以实现不同的 goroute 之间的相互通信。
-
函数可以返回多个值。
Go 的 hello world
下面书写一个 go 版本的 hello world
package main
import "fmt"
func main(){
fmt.Println("hello ,world")
}
- 编译
go build hello.go
:
参数说明:
-o 指定编译后文件的名称
# 通过 build 命令将文件转化为二进制
# 指向完下面的命令后,会生成一个 hello 的一个文件
$ go build hello.go
- 运行
$ ls
hello hello.go
$ hello
hello, world
说明
go 为了方便测试,也可以通过 go run
直接进行运行,类似于执行一个脚本文件,但是不能用于生产环境。
这个过程中也是会编译成二进制源码的,因此速度相对来说较慢。
如果此时 demo 里面有错误,编译的时候会进行提示,例如如下代码:
package main
// 引入 fmt 就表示可以使用 fmt 包的函数
import "fmt"
func main(){ // 表示主函数(程序的入口)
fmt.Println("hello, world")
fmt.Pri232("hello, world")
}
编译:
$ go build hello.go
# command-line-arguments
./hello.go:7:2: undefined: fmt.Pri232
Golang 的执行流程分析
- 如果是对源码编译后,再执行,Go 的执行流程如下图:


-
如果我们先编译生成了可执行文件,那么我们可以将该可执行文件拷贝到没有go开发环境的机器
上,仍然可以运行 -
如果我们是直接
go run
执行 go 源代码,那么如果要在另外-一个机器上这么运行,也需要go开发环境,
否则无法执行。 -
在编译时,编译器会将程序运行依赖的库文件包含在可执行文件中,所以,可执行文件变大了很多。
开发注意事项
杂项
- Go源文件以"go"为扩展名。
- Go应用程序的执行入口是main()函数。
- Go语言严格区分大小写。
- Go方法由一条条语句构成,每个语句后不需要分号(Go语言会在每行后自动加分号),这也体现出Golang的简洁性。
- Go编译器是一行行进行编译的,因此我们一行就写一条语句,不能把多条语句写在同一个,否则报错
- go语言定义的变量或者 import 的包如果没有使用到,代码不能编译通过。

- 大括号都是成对出现的,缺一不可。
- Go 当中,一个包只能有一个 main 函数,如果有同名的话,会报错。
解决方案:新建一个文件夹

- vscode 安装 go 插件失败。
解决办法:
# 打开 GO111MODULE, 并切换为国内源
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
重新打开 vscode,选择一键安装。
更多可以参考此篇文章:
- 如果我们希望自己的 GO 项目在
GOPATH/src
外面进行运行的话,需要基于 Go Module 来进行创建:
- 例如我们想要在
~/Coding/tour
这个目录下创建一个工程
~/Coding/go ⌚ 15:00:55
$ mkdir tour
~/Coding/go ⌚ 15:01:02
$ cd tour
~/Coding/go/tour ⌚ 15:01:10
$ go mod init flysnow.org/tour
go: creating new go.mod: module flysnow.org/tour
~/Coding/go/tour ⌚ 15:01:20
$ ls
go.mod
~/Coding/go/tour ⌚ 15:01:22
$ cat go.mod
module flysnow.org/tour
go 1.16
这个时候我们打开 VSCODE,写入如下内容:

package main
import (
"fmt"
"flysnow.org/tour/model"
)
func main() {
fmt.Println("Hello World")
fmt.Printf("model.Hero: %v\n", model.Hero)
}
基础内容
1. Go 转义字符
字符 | 说明 |
---|---|
\t | 一个制表位,实现对其功能 |
\n | 换行符 |
\\ | 一个 \ |
\" | 一个“ |
\r | 一个回车,fmt.Println(“天龙八部雪山飞狐/r张飞”) |
2. 注释
行注释
// 大家好,我是注释内容
块注释
/*
大家好,我是块注释
*/
3.代码格式规范
格式规范
需要正确的进行代码的缩进和对其,可以通过命令行的方式来进行格式的调整:
gofmt -w main.go
-w
参数:该参数用来将格式化完成的代码写回到 main.go 这个文件中。如果没有-w
参数的话,只能打印到控制台上。
代码风格
函数的括号只能以一种方式来进行,否则编译器报错:
// 正确的形式
func main() {
// 演示转义字符的使用
fmt.Println("tom\rjack")
fmt.Println("C:\\Users\\Administrator\\Desktop")
fmt.Println("tom 说:\"i love you\"")
}
Go 语言不允许使用如下的方式来书写:

4. 文档内容
-
官方的接口文档位于:接口文档
-
官方的简易教程文档:A Tour of Go
-
Mac 系统中查看 Go 的源码,位于:
/opt/homebrew/Cellar/go/1.16.5/libexec/src # 里面是一堆包

1. 单个变量的声明方式
- 声明变量并指定类型
func main() {
// 定义变量, 声明变量后, 若不赋值, 使用默认值,int 的默认值为 0
var i int
// 给 i 赋值
i = 10
fmt.Println("i=", i)
}
打印输出:
i= 10
- 根据值自行判定变量类型
func main() {
var num = 10.11
fmt.Println("num = ", num);
}
打印输出:
num = 10.11
- 省略
var
进行声明。如果声明时就直接赋值,可以省略数据类型。
注意: :=
左侧的变量至少一个是已经声明过的,否则会导致编译错误。
func main() {
name := "tom"
// 下面的方式等价于 var name string; name ="tom"
fmt.Println("name=",name)
}
2. 一次性声明多个变量
- 通过
var
一次性声明多个变量,并使用默认值
func main() {
var n1, n2, n3 int
fmt.Println("n1=",n1,"n2=",n2,"n3=",n3)
}
打印输出:
n1= 0 n2= 0 n3= 0
- 根据值进行类型推到
func main() {
n1, name, n3 := 100, "tom~", 888
fmt.Println("n1=", n1, "name=", name, "n3=", n3)
}
打印输出:
n1= 100 n2 0 name= tom~
- 通过括号进行多个声明:
var (
n3 = 300
n4 = 900
name2 = "marry"
)
func main() {
// 输出全局变量
fmt.Println("n1=", n1, "n2", n2, "name=", name)
}
打印输出:
n3= 888 n4 0 name2= marry
总结:因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。var
形式的声明语句往往是用于需要显式指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
3. 注意事项
- 变量可以在作用域内进行改变,但是不能改变其变量类型

func main() {
var i int = 10
i = 30
i = 50
// i = 1.2 // 不能改变数据类型
fmt.Println("i=", i)
// 变量在同一个作用域内(在一个函数或者在代码块里)内不能重名
var i int = 59
}

4. 变量中 + 的使用
在 Go 当中,+ 和 Python 中的用法类似:
- 如果两边都是数值类型的话,做加法运算。
- 当左右两边都是字符串的话,则做字符串拼接。
package main
import "fmt"
func main() {
var i = 1
var j = 2
var r = i + j
fmt.Println("r=", r)
var str1 = "hello"
var str2 = "world"
var res = str1 + str2
fmt.Println("res=", res)
}
GO 语言的数据类型

一位表示 8 个字节
类型 | 有无符号 | 占用存储空间 | 表示范围 |
---|---|---|---|
int8 | 有 | 1字节 | -128~127 |
int16 | 有 | 2字节 | − 2 15 -2^{15} −215 ~ 2 15 − 1 2^{15}-1 215−1 |
Int32 | 有 | 4字节 | − 2 31 -2^{31} −231 ~ 2 31 − 1 ~2^{31} - 1 231−1 |
int64 | 有 | 8字节 | − 2 63 -2^{63} −263 ~ 2 63 − 1 2^{63} - 1 263−1 |
- 测试 int8 的范围
package main
import "fmt"
func main() {
var i int = 1
fmt.Println("i=", i)
// 测试一下 int8 的范围 -128~ 127
var j int8 = 128
fmt.Println("j=", j)
}

# 可以看到上面已经开始出现报错的红色下划线信息了
$ go run main.go
# command-line-arguments
./main.go:9:6: constant 128 overflows int8
- 测试 uint8(0-255) 的范围
package main
import "fmt"
func main() {
// 测试一下 uint8 的范围(0-255),其它的 uint16,uint32,uint64 一样
var k int = -1
fmt.Println("k=", k)
}
打印输出:
$ go run main.go
# command-line-arguments
./main.go:13:6: constant -1 overflows uint8
类型 | 有无符号 | 占用存储空间 | 表示范围 | 备注 |
---|---|---|---|---|
int | 有 | 32位系统 4 个字节 32位系统 8 个字节 |
−
2
31
-2^{31}
−231 ~
2
31
−
1
~2^{31} - 1
231−1 − 2 63 -2^{63} −263 ~ 2 63 − 1 ~2^{63} - 1 263−1 | |
uint | 无 | 32位系统 4 个字节 32位系统 8 个字节 | 0 ~
2
32
−
1
~2^{32} - 1
232−1 0 ~ 2 64 − 1 2^{64} - 1 264−1 | |
rune | 有 | 与 int32 一样 | − 2 31 -2^{31} −231 ~ 2 31 − 1 2^{31} - 1 231−1 | 等价 int32, 表示一个 unicode编码 |
byte | 无 | 与 int8 一样 | 0~255 | 当要存储字符时, 选用 byte |
package main
import "fmt"
func main() {
// int, uint, rune, byte 的使用
var a int = 8900
fmt.Println("a=", a)
var b uint = 0 // -1 的话会抛出越界异常, uint 的范围是 0 - 2^{32} - 1
var c uint = 255
fmt.Println("b = ", b, "c = ", c)
var d rune = 0
fmt.Println("d=", d)
}

a = 8900
b = 0 c = 255
d= 0
整型的使用细节:
- Golang 各整数类型分:有符号 和 无符号 , int uint 的大小和系统有关。
- Golang 的整型默认声明为 int 型
package main
import (
"fmt"
"unsafe"
)
func main() {
// 整型的使用细节
var n1 = 100 // n1 是什么类型
// fmt.Printf() 可以用于格式化输出
fmt.Printf("n1 的类型是 %T\n", n1)
}
打印输出:
n1 的类型是 int
- 如何在程序中查看某个变量的字节大小和数据类型
package main
import (
"fmt"
"unsafe"
)
func main() {
var i int = 1
fmt.Println("i=", i)
// 如何在程序中查看某个变量的字节大小和数据类型(使用较多)
var n2 int64 = 10
fmt.Printf("n2 的类型是 %T n2 占用的字节数是 %d", n2, unsafe.Sizeof(n1))
}
打印输出:
n2 的类型是 int64 n2 占用的字节数是 8
- Golang 程序中整型在使用时,遵守保小不保大的原则,即:在保证程序正确运行情况下,尽量使用空间小的数据类型。【如:年龄】
package main
import (
"fmt"
"unsafe"
)
func main() {
// 保小不保大的原则
var age byte = 90
fmt.Println("age = ", age)
}
打印输出:
age = 90
- bit:计算机中的最小存储单位。byte 是计算机中的基本存储单元:1 byte= 8 bit
2.浮点型
浮点表示小数,简单使用的话,和 Python 没有太大的差别,默认的浮点类型是 float64
package main
import "fmt"
// 演示小数类型的使用
func main() {
var price float32 = 89.12
var n1 = 3.2
fmt.Printf("n1 的类型是: %T\n", n1)
fmt.Println("price = ", price)
}
打印输出:
$ go run main.go
n1 的类型是: float64
price = 89.12
小数类型分类
类型 | 占用存储空间 | 表示范围 |
---|---|---|
单精度 float32 | 4 字节 | -3.403E38 ~ 3.4003E38 |
双精度float64 | 8字节 | -1.798E308 ~ 1.798E308 |
- 浮点树在机器的存放形式的简单说明:浮点数 = 符号位 + 指数位 + 尾数位
- 说明:浮点数都是有符号的
package main
import "fmt"
// 演示小数类型的使用
func main() {
var num1 float32 = 0.000089
var num2 float64 = -7809656.09
fmt.Println("num1 = ", num1, "num2= ", num2)
}
打印输出:
$ go run main.go
n1 的类型是: float64
price = 89.12
num1 = 8.9e-05 num2= -7.80965609e+06
- 尾数可能丢失,造成精读损失。 -123.0000901
package main
import "fmt"
// 演示小数类型的使用
func main() {
// 尾数可能丢失, 造成精读损失 -123.0000901
var num3 float32 = -123.0000901
var num4 float64 = -123.0000901
fmt.Println("num3 = ", num3, "num4=", num4)
}
说明:float64 的精读比 float32 的要准确。 如果精度要求比较高, 使用 float64
- Go 和 Python 一样,也同样可以通过科学计数法的形式来定义浮点类型。
package main
import "fmt"
// 演示小数类型的使用
func main() {
// 十进制形式
num6 := 5.12
num7 := .123
fmt.Println("num6:", num6, "num7=", num7)
// 科学计数法形式
num8 := 5.1234e2
num9 := 5.1234e2
num10 := 5.1234e-2
fmt.Println("num8=", num8, "num9=", num9, "num10=", num10)
}
打印输出:
num6: 5.12 num7= 0.123
num8= 512.34 num9= 512.34 num10= 0.051234
总结:
-
Golang 的浮点型默认声明方式为 float64 类型。
-
开发当中推荐使用 float64 。
3.字符类型
Go 语言的字符型通过 byte
来定义,对应 Python 语言的 bytes。
Go 当中的 byte 是 uint8 的别名,其中一个区别是 Go 保存汉字的话,默认采用的是 ASCII 表,会发生溢出问题。
Python 对应的 bytes 使用示例
In [64]: c1 = bytes("a",encoding="ascii")
In [65]: ord(c1)
Out[65]: 97
In [66]: c2 = bytes("0",encoding="ascii")
In [67]: ord(c2)
Out[67]: 48
In [68]: c1.decode()
Out[68]: 'a'
In [69]: c2.decode()
Out[69]: '0'
In [74]: ord('北')
Out[74]: 21271
GO 当中 byte 的使用示例
package main
import "fmt"
func main() {
var c1 byte = 'a'
var c2 byte = '0'
// 当我们直接输出 byte 值时, 输出的是它对应的 ASCII 码值
fmt.Println("c1 = ", c1, "c2 = ", c2)
// 如果我们希望输出对应的字符,使用格式化输出
fmt.Printf("c1=%c,c2=%c\n", c1, c2)
// var c3 byte = "北" 北对应的编码超过了 ASCII 码表的值, 会溢出
var c3 int = '北'
fmt.Printf("c3=%c, c3对应的码值为:%d, 类型是 %T", c3, c3, c3)
}
打印输出:
$ go run main.go
c1 = 97 c2 = 48
c1=a,c2=0
c3=北, c3对应的码值为:21271, 类型是 int
代码说明:
- 如果我们保存的字符在 ASCII 码表的,比如[0-1, a-z, A-Z…] 直接可以保存到 byte。
- 如果我们保存的字符对应码值大于 255,这时我们可以考虑使用 int 类型保存。
- 如果我们需要安装字符的方式输出,这时我们需要格式化输出,即
fmt.Printf(%c,c1)
。
总结:
在 Go 当中,字符的本质是一个整数,直接输出是一个 UTF-8 的码值。因此我们可以给某个变量赋一个数字,然后按格式化输出 %c
,会输出对应的 unicode 字符。
-
如果针对一个中文进行字符的转化,因为 ASCII 码表的数量不够,需要采用 int 类型来进行赋值:
package main import "fmt" func main() { var c3 int = '北' fmt.Printf("c3=%c, c3对应的码值为:%d, 类型是 %T\n", c3, c3, c3) }
-
GO 里面允许使用转义字符
\n
来将其后的字符转变为特殊字符型常量。例如:package main import "fmt" func main() { var c4 = '\n' fmt.Printf("c4= %d", c4) }
-
字符类型是可以进行运算的,相当于一个整数,因为它都有对应的 Unicode 码。
package main import "fmt" func main() { // 字符类型是可以运算的,相当于一个整数,运算时是按照码值运行 var n1 = 10 + 'a' fmt.Println("n1=",n1) }
打印输出:
n1= 107
-
GO 语言的编码都统一成了 utf-8,几乎不会出现编码乱码的困扰了。
对于 Python 来说,可以选择不同的编码方式,其本质是一个 bytes 对象。
4.布尔类型
GO 里面的布尔类型只能允许取值 TRUE 或者 FALSE。
bool 类型占一个字节。

字符串就是一串固定长度的字符连接起来的字符序列。 Go的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
基本使用
package main
import (
"fmt"
)
func main() {
var address string = "长城我来了 hello world"
fmt.Println(address)
}
打印输出:
$ go run main.go
长城我来了 hello world
不同的字符串定义形式
1)双引号,会识别转义字符
2)反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果
package main
import (
"fmt"
)
func main() {
var address string = "长城我来了 hello world"
fmt.Println(address)
// 字符串一旦赋值了,就不能更改了。为不可变类型
// var str = "hello"
// str[0] = 'a' // 这里不能修改 str 的内容
// 字符串的两种表示形式3(1)双引号,会识别转义字符3(2)反引号,
// 以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、
// 输出源代码等效果
str2 := "abc\nabc"
fmt.Println(str2)
str3 := `
package main
import "fmt"
// 演示小数类型的使用
func main() {
var price float32 = 89.12
var n1 = 3.2
fmt.Printf("n1 的类型是: %T\n", n1)
fmt.Println("price = ", price)
}
`
fmt.Printf("%T %s",str3,str3)
}
字符串的拼接
go 的字符串拼接和 Python 类似,通过 +
来进行拼接。
package main
import (
"fmt"
)
func main() {
// 字符串的拼接方式
var str = "hello" + "world"
str += "haha!"
fmt.Println("str:", str)
// 字符串的换行
str4 := "hello" + "world" + "hello" + "world" + "hello" +
"world" + "hello" + "world"
fmt.Println("str4:", str4)
}
打印输出:
str: helloworldhaha!
str4: helloworldhelloworldhelloworldhelloworld
区别的地方就是,如果需要拼接的字符串长度过长的话,GO 要求将 +
放在换行符之前,而 Python 则是需要在换行的地方加上 \
。
6. 指针
指针就是针对某个内存空间的一个引用。
- 获取变量的地址,用 &, 比如
var num int
, 获取 num 的地址: &num。
内存图:

- 对于指针变量来说,其存放的是一个地址,对应里面存放地址的内存空间才是真正的值。
- 获取指针指向的值,使用
*ptr
package main
import "fmt"
// 指针类型
func main() {
var i int = 10
fmt.Printf("i的地址=%p\n", &i)
// 1. ptr 是一个指针变量
// 2. ptr 的类型是 *int
// 3. ptr 本身的值是 &i
var ptr *int = &i
fmt.Printf("ptr=%v\n", ptr)
fmt.Printf("ptr 的地址是=%p", &ptr)
fmt.Printf("ptr 指向的值是:%v", *ptr)
}
$ go run main.go
i的地址=0x140000b6008
ptr=0x140000b6008
ptr 的地址是=0x140000ba020ptr 指向的值是:10

这里我们可以将指针这个概念和门牌号进行类比:
i
指向了绿色这个门,对应的门牌号是 0x140000b6008
,里面住的是 10 号同学。
这个时候 ptr
和大家开了一家房屋中介,它把 i
指向的大门地址写成在了一张纸上,放在了蓝色小屋里面,蓝色小屋的门牌号是 0x140000b6008
。
如果我们想要拿到 ptr 里面的地址纸条,那么fmt.Printf(“%v”,ptr) 就可以直接获取了。
如果希望通过 ptr 来找到绿色屋子里面的 10 号同学,那么我们需要通过 *fmt.Printf(“%v”, ptr) 来获取。也就是说,需要加一个 *
。
坑点:
package main
import "fmt"
func main() {
var a int = 300
var ptr1 *int = a // 错误,这里接受的应该是一个指针变量 应该改成 &a
var ptr2 *float32 = &a // 错误, 这里类型不匹配,a 的类型为 int,应该用 *int 来接收
fmt.Printf("%v", ptr)
}
说明:
- 每一种类型的指针都有其对应的类型,形式为 *数据类型,比如 int 对应的指针就是 *int,float32 对应的类型就是 *float,一次类推。
- 类型包括:int,float,bool,string,数组和结构体【struct】。
基本数据类型的默认值
数据类型 | 默认值 |
---|---|
整型 | 0 |
浮点型 | 0 |
字符串 | “” |
布尔类型 | false |
在 GO 当中,数据类型都有一个默认值,当程序员没有赋值时,就会保留默认值。在 GO 当中,默认值又被称为 0 值。
package main
import (
"fmt"
)
func main() {
var a int
var b float32
var c float64
var isMarried bool
var name string
// 这里 %v 表示按变量的值来输出
fmt.Printf("a = %d, b = %f, c= %f, isMarried = %v, name = %v",a,b,c,isMarried,name)
}
打印输出:
a = 0, b = 0.000000, c= 0.000000, isMarried = false, name =
基本数据类型的相互转化
1 . 数值类型的转化
GO 的数据类型转化和 Python 类似,都是要进行显示声明的:
package main
import (
"fmt"
)
func main() {
var i int32 = 100
// 希望将 i=> float
var n1 float32 = float32(i)
var n2 int8 = int8(i)
var n3 int64 = int64(i)
fmt.Printf("i = %v, n1= %v, n2= %v, n3 = %v", i, n1, n2, n3)
}
打印输出:
$ go run main.go
i = 100, n1= 100, n2= 100, n3 = 100
说明:
- 被转化的变量是不会发生变化的,也就是对新的值另外开辟一个空间。
- 在转化的过程中,如果将
int64
转化为int64
,编译时不会报错,而是按二进制位溢出处理,和我们想象的结果不太一样。
例如下面这段代码:
package main
func main() {
// 测试
var n1 int32 = 12
var n2 int64
var n3 int8
n2 = n1 + 20 // int32 ---> int64 错误
n3 = n1 + 20 // int32 ---> int8 错误
}
因此必须进行显式的转化:
package main
import "fmt"
func main() {
// 测试
var n1 int32 = 12
var n2 int64
var n3 int8
n2 = int64(n1) + 20 // int32 ---> int64 错误
n3 = int8(n1) + 20 // int32 ---> int8 错误
fmt.Printf("n1= %d, n2=%d, n3= %d",n1,n2,n3)
}
关于 int
类型的坑,还有编译通过和不通过的问题:
package main
import "fmt"
func main() {
var n1 int32 = 12
var n3 int8
var n4 int8
n4 = int8(n1) + 127 // 编译不通过,但是
n3 = int8(n1) + 128 // 编译不通过
fmt.Println(n3,n4)
}

从上面的代码来看,如果数值小于对应的数值类型,编译时不会出错,而是在结果计算的时候将值进行溢出处理。但是如果超过了对应的数值类型最大值,则会编译失败。
2.其他类型转字符串类型
- GO 语言的占位符
普通占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%v | 相应值的默认格式。 | Printf("%v", people) | {zhangsan}, |
%+v | 打印结构体时,会添加字段名 | Printf("%+v", people) | {Name:zhangsan} |
%#v | 相应值的Go语法表示 | Printf("#v", people) | main.Human{Name:“zhangsan”} |
%T | 相应值的类型的Go语法表示 | Printf("%T", people) | main.Human |
%% | 字面上的百分号,并非值的占位符 | Printf("%%") | % |
布尔占位符
占位符 | 说明 | 举例 | 输出 |
---|---|---|---|
%t | true 或 false | Printf("%t", true) | true |
整数占位符
浮点数和复数的组成部分(实部和虚部)
- 使用
Sprintf
进行转化
package main
import (
"fmt"
)
func main() {
var num1 int = 99
var num2 float64 = 23.456
var b bool = true
var myChar byte = 'h'
var str string // 空的 string
// 使用第一种方式进行转化
str = fmt.Sprintf("%d", num1) // Sprintf 表示格式化并返回一个字符串,不做输出打印
fmt.Printf("str type %T str=%q\n", str, str) // %q占位符表示字符的面值,并用引号(quote)包裹
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%t", b)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%c", myChar)
fmt.Printf("str type %T str= %q\n", str, str)
}
打印输出:
$ go run main.go
str type string str="99"
str type string str="23.456000"
str type string str="true"
str type string str= "h"
- 使用
strconv
包的函数func FormatInt(i int64, base int) string
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
func FormatBool(b bool) string
func Itoa(i int) string
- 等价于 FormatInt(int64(i), 10)
package main
import (
"fmt"
"strconv"
)
func main() {
// 第二种方式 strconv 函数
var num3 int = 99
var num4 float64 = 23.456
var b2 bool = true
str = strconv.FormatInt(int64(num3), 10)
fmt.Printf("str type %T str=%q\n", str, str)
// f 表示接受的是 float, 10: 表示小数保留 10 位, 64 表示这个小数是 float64
str = strconv.FormatFloat(num4, 'f', 10, 64)
fmt.Printf("str type %T str=%q\n", str, str)
str = strconv.FormatBool(b2)
fmt.Printf("str type %T str =%q\n", str, str)
// strcov 包中有一个 Itoa 函数, 等价于 FormatInt(int64(i), 10)
var num5 = 4567
str = strconv.Itoa(num5)
fmt.Printf("str type %T, str is %q\n", str,str)
}
打印输出:
str type string str="99"
str type string str="23.4560000000"
str type string str ="true"
str type string, str is "4567"
使用建议:fmt 这个包底层使用了反射的机制,效率较低,因此符号转化的时候建议使用strconv 这个包。
3.字符串转其它数据类型
这里我们使用的也是 strconv 包的函数:
func ParseBool(str string) (bool, error)
package main
import (
"fmt"
"strconv"
)
func main() {
var str string = "true"
var b bool
// 和 Python一样,通过 _ 来忽略值
b, _ = strconv.ParseBool(str)
fmt.Printf("b type %T b =%v\n", b, b)
}
打印输出:
b type bool b =true
func ParseFloat(s string, bitSize int) (float64, error)
package main
import (
"fmt"
"strconv"
)
func main() {
var str3 string = "123.456"
var f1 float64
f1, _ = strconv.ParseFloat(str3, 64)
fmt.Printf("f1 type is %T, f1 = %v\n", f1, f1)
}
打印输出:
f1 type is float64, f1 = 123.456
-
func ParseInt(s string, base int, bitSize int) (i int64, err error)
-
func ParseUint(s string, base int, bitSize int) (uint64, error)
base
参数表示以什么进制的方式去解析给定的字符串,有效值为0、2 - 36。当 base=0 的时候,表示根据 string 的前缀来判断以什么进制去解析:0x
开头的以16进制的方式去解析,0
开头的以8进制方式去解析,其它的以 10 进制方式解析。
而当 base 为 2 - 36 中的某一个数时,例如 base = 5,那么就按 5 为进制来进行解析。
bitSize
参数表示转换为什么位的 int/uint,有效值为0、8、16、32、64,分别表示转化为 int,int8,int16 和 int64 的数据类型。当bitSize=0的时候,表示转换为 int 或 uint 类型。例如 bitSize = 8 表示转换后的值的类型为int8 或 uint8。
经测试验证,bitSize 这里只能填入 64 这个数字,填入别的数字无法进行转化【因为返回的只能是 int64 】。因此如果我们希望获得其它类型的数字的话,需要进行进一步的强转:num = int32(num5)
示例:
package main
import (
"fmt"
"strconv"
)
func main() {
var str2 string = "123456789"
var n1 int64
n1, _ = strconv.ParseInt(str2,36,64) // 这里只能转化为 64 位的,即 int64
fmt.Printf("n1 type %T, n1=%v\n",n1,n1)
i, _ := strconv.ParseInt("23", 5, 64)
println(i) // 13
}
打印输出:
$ go run main.go
b type bool b =true
n1 type int64, n1=2984619134745
13
注意事项:
如果给定的字符串没有成功转化为整数,Glang 会将它转化为 0【对应数据类型的默认值】。
package main
import (
"fmt"
"strconv"
)
func main() {
// 注意事项:
var str4 string = "hello"
var n3 int64 = 11
n3, _ = strconv.ParseInt(str4, 10, 64)
fmt.Printf("n3 type is %T, n3 = %v", n3, n3)
}
值类型和引用类型
值类型:变量直接存储值,内存通常在栈中分配
- int系列,float系列,bool,string,数组和结构体【struct】。
示意图:

引用类型:变量存储的是-个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
- 指针、slice 切片、map、管道 chan,interface 等都是引用类型。

内存的展区和堆区示意图:

GO 的命名规范
1. 标识符的命名规则
- 由26个英文子母大小与,0-9,_ 组成
- 数字不可以开头。var num int //ok var 3num int //error
- Golang中严格区分大小写。
- 标识符不能包含空格。
- 下划线 "_ " 本身在Go中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但是它对应的值会被忽略(比如:忽略某个返回值)。所以仅能被作为占位符使用,不能作为标识符使用。
- 不能以系统保留关键字作为标识符【共 25 个】,比如break, if等等.
系统保留关键字
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
clan | else | goto | package | switch |
const | Fallthrough | if | range | type |
continue | for | import | return | Var |
预定义标识符
append | bool | byte | cap | close | complex |
---|---|---|---|---|---|
complex64 | complex128 | uint16 | copy | false | float32 |
float64 | imag | int | int8 | int16 | uint32 |
Int32 | int64 | ioda | len | make | new |
nil | panic | uint64 | println | real | |
revover | string | true | uint | uint8 | uintprt |
2. 标识符命名注意事项
- 包名:保持 package 的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,不要和标准库不要冲突,例如 fmt。

- 变量名、函数名、常量名,采用驼峰法
举例:
var stuName string = "tom"
var goodPrice float32 = 1234.5
- 如果变量名、函数名、常量名首字母大写,则可以被其他的包访问:如果首字母小写,则只能在本包中使用( 注:可以简单的理解成,首字母大写是公开的,首字母小写是私有的) ,在 golang 没有 public ,private 等关键字。
Go 运算符
算数运算符
主要有如下运算法:
-
+、-、*、/、%、++、- -。
-
自增 ++
-
自减 –
这里比较奇葩的就是 %
【模,或者说是取余】:
在正数的情况下,取模运算就是按正常去取余数即可。但是在负数的情况下,情况就会有所区别了。
需要遵循这个公式:a % b = a - (a / b) * b
。
package main
import (
"fmt"
)
func main() {
// 演示 % 的使用
// 看一个公式 a % b = a - (a / b) * b
fmt.Println("10%3=", 10 % 3)
fmt.Println("-10%3=", -10 % 3) // -10 - (10) / 3 = -10 - (-9)
fmt.Println("10%-3=", 10 % -3)
fmt.Println("-10%-3=", -10 % -3) // =?
}
打印输出:
10 % 3 = 1
-10 % 3 = -1
10 % -3 = 1
-10 % -3 = -1
当然此时我们打开 Python Shell,会发现结果和 Go 里面有所区别:

- 对于除号"", 它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃
小数部分。例如: x=19/5.结果是 3 - 当对一个数取模时,可以等价a%b=a-ab*b ,这样我们可以看到取模的一个本质运算。
- Golang 的自增自减只能当做一个独立语言使用时,不能用于赋值或者条件语句:
package main
import "fmt"
func main() {
var i int = 8
var a int
i++
//a = i++ // 错误
b = i--
}
关系运算符
- 关系运算符的结果都是 bool 型,要么是 true,要么是 false
- 关系表达式经常用在 if 结构的条件中或 循环结构 的条件中。
package main
import (
"fmt"
)
func main() {
// 演示关系运算符的使用˜
var n1 int = 9
var n2 int = 8
fmt.Println(n1 == n2) // false
fmt.Println(n1 != n2) // true
fmt.Println(n1 > n2) // true
fmt.Println(n1 >= n2) // true
fmt.Println(n1 <= n2) // false
flag := n1 > n2
fmt.Println("flag=", flag)
}
打印输出:
false
true
true
true
false
flag= true
逻辑运算符
这里和 Python 类比,需要关注的就是它 需要加上俩花括号{}
:
package main
import "fmt"
func main() {
var age int = 40
// &&
if age > 30 && age < 50{
fmt.Println("ok1")
}
if age > 30 && age < 40 {
fmt.Println("ok2")
}
// ||
if age > 30 || age < 50{
fmt.Println("ok3")
}
if age > 30 || age < 40{
fmt.Println("ok4")
}
// !
if age > 30 {
fmt.Println("ok5")
}
if !(age > 30){
fmt.Println("ok6")
}
}
打印输出:
ok1
ok3
ok4
ok5
赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算,将一个表达式的值赋给一个左值 | C=A+B 将 A+B 表达式结果赋给 C |
+= | 相加后赋值 | C += A 等价于 C = C + A |
-= | 相减后赋值 | C -= A 等价于 C = C - A |
*= | 相乘后赋值 | C *= A 等价于 C = C * A |
/= | 相除后赋值 | C /= A 等价于 C = C / A |
%= | 求余后赋值 | C %= A 等价于 C = C % A |
package main
import "fmt"
func main() {
// 赋值运算符的使用演示
// var i int
// i = 10 // 基本赋值
// 有两个变量,a 和 b,要求将其进行交换,最终打印结果
// a = 9, b = 2 ==> a =2 b = 9
a := 9
b := 2
fmt.Printf("交换前的情况是: a = %v, b = %v\n", a, b)
a, b = b, a
fmt.Printf("交换后的情况是: a = %v, b = %v\n", a, b)
// 复合赋值的操作
a += 17
fmt.Println("a=", a)
}
打印输出:
交换前的情况是: a = 9, b = 2
交换后的情况是: a = 2, b = 9
a= 19
另一种交换变量的方法:
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
a = a + b
b = a - b // 此时 b 里面存放的值是 a
a = a - b
fmt.Printf("a = %v, b = %v", a, b)
}
打印输出:
a = 20, b = 10
运算符的优先级

位运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A | B) 结果为 61, 二进制为 0011 1101 |
^ | 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
<< | 左移运算符"<<“是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 |
>> | 右移运算符">>“是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
- Golang中有2个移位运算符:
>>、<<右移和左移,运算规则:
右移运算符>>:低位溢出,符号位不变并用符号位补溢出的高位
左移运算符<<:符号位不变,低位补0
其他运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a,将给出变量的实际地址 |
* | 指针变量 | *a;是一个指针变量 |
package main
import "fmt"
func main() {
a := 100
var b *int = &a
fmt.Printf("a的地址是:%v\n", &a)
fmt.Printf("a的值是:%v", *b)
}
打印输出:
a的地址是:0x1400011c000
a的值是:100
注意:Golang 中没有三元运算符!
杂项
控制台的输入输出
fmt.Scanln(地址)
package main
import "fmt"
func main() {
var name string
var age byte
var sal float32
var isPass bool
// 方式1: fmt.Scanln
fmt.Printf("请输入姓名")
fmt.Scanln(name)
fmt.Printf("请输入年龄")
fmt.Scanln(&age)
fmt.Printf("请输入薪水")
fmt.Scanln(&sal)
fmt.Printf("请输入是否通过考试")
fmt.Scanln(&isPass)
fmt.Printf("名字是 %v,\n 年龄是 %v, \n 薪税是 %v, \n 是否通过考试 %v\n", name, age, sal, isPass)
}
打印输出:
请输入姓名xzc
请输入年龄12
请输入薪水23.2
请输入是否通过考试true
名字是 xzc,
年龄是 12,
薪税是 23.2,
是否通过考试 true
fmt.Scanf(地址符)
package main
import "fmt"
func main() {
var name string
var age byte
var sal float32
var isPass bool
// 方式二: fmt.Scanf
fmt.Println("请输入您的姓名,年龄,薪水,是否通过考试,使用空格隔开")
fmt.Scanf("%s %d %f %t", &name, &age, &sal, &isPass)
fmt.Printf("名字是 %v, \n年龄是 %v, \n 薪水是 %v,\n 是否通过考试%v\n",name, age,sal, isPass)
}
$ go run main.go
请输入您的姓名,年龄,薪水,是否通过考试,使用空格隔开
xzc 19 34543.1 false
名字是 xzc,
年龄是 19,
薪水是 34543.1,
是否通过考试false
原码、反码、补码
对于有符号的而言:
- 二进制的最高位是符号位: 0表示正数,1表示负数
- 1==> [0000 0001] -1===>[1000 0001]
-
正数的原码,反码,补码都一样
-
负数的反码=它的原码符号位不变,其它位取反(0->1,1->0)
- 1===>原码 [0000 0001] 反码[0000 0001] 补码[0000 0001]
- -1===>原码 [1000 0001] 反码[11111110] 补码[1111 1111]
- 负数的补码 = 它的反码 + 1
- 0 的反码,补码都是 0
- 在计算机运算的时候,都是以补码的方式来运算的.
package main
import "fmt"
func main() {
fmt.Println(2&3) // 2
fmt.Println(2|3) // 3
fmt.Println(2^3) // 3
fmt.Println(-2^2) // -4
}
下一篇:Golang 基础
参考内容:尚硅谷