type
status
date
slug
summary
tags
category
icon
password
快速入门
微服务和云原生已经成为主流趋势,而 Go 语言(Golang)正是最适合用来开发这类系统的语言之一。Go 是一种高性能的编译型语言,可以非常方便地生成跨平台的可执行文件。不需要像 Java 一样安装虚拟机,也不像 C 那样需要手动管理内存。Go 兼具 C 的高性能与 Java 的自动内存管理优点,让开发者无需担心复杂的内存分配,同时获得编译型语言带来的部署效率与运行性能。很多云原生核心组件,如 Kubernetes、Docker、etcd 等,都是由 Go 编写的。Go 已成为云原生生态的主流开发语言。
注释
Go 语言支持 C 风格的块注释
/* */ 和 C++ 风格的行注释//。 行注释更为常用,而块注释则主要用作包的注释,当然也可在禁用一大段代码时使用。标识符
一个名字,本质是个字符串,用来指代一个值,比如我叫张三,张三就是标识符。标识符只能是大小写字母、数字、下划线,不能以数字开头,也不能是关键字。
在 Go 语言中,下划线
_ 被称为空白标识符,它是一种特殊的占位符,用于接收但忽略某个值。由于 _ 没有名称,也不会保存任何值,因此也被称为匿名占位符。可以把它类比为类 Unix 系统中的 /dev/null ,所有传给它的东西都会被吞掉。变量
变量是几乎所有编程语言中最基本的组成元素。从本质上说,变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。
变量代表可变的数据类型,它在程序执行的过程中可能会被一次甚至多次修改。在 Go 语言中,通过 var 声明语句来定义一个变量,定义的时候需要指定这个变量的类型,然后再为它起个名字,并且设置好变量的初始值。所以 var 声明一个变量的格式如下:
下面我们来运行一个简单的示例
Go 语言中定义的变量必须使用,否则无法编译通过,防止定义了变量不使用,导致浪费内存的情况。如果变量未被赋值,Go 会自动地将其初始化,赋值该变量类型的零值
如果你运行该程序,你会看到如下输出:
Go 能够通过一条语句声明多个变量。声明多个变量的语法如下:
使用上述语法,下面的程序声明不同类型的变量
运行上面的程序会产生输出
Go 也支持一种声明变量的简洁形式,称为简短声明,使用了
:= 操作符。声明变量的简短语法是 变量名:=表达式。简短声明有几个特点:只能在函数内部使用,不能用于定义全局变量;不能显式指定变量类型,变量类型由编译器根据表达式的结果自动推导。一般用于临时变量或快速赋值场景,使代码更加简洁。运行上面的程序,你可以看见下面的输出
Go 语言具有类型推导功能,所以也可以不去刻意地指定变量的类型,而是让 Go 语言自己推导。如果变量有初始值,那么 Go 能够自动推断具有初始值的变量的类型。在下面的例子中,我们省略了变量的类型,Go 依然推断出了它们的类型。
执行输出结果
在 Go 语言中,变量初始化和变量赋值是两个不同的概念,变量初始化集变量声明和赋值为一条语句,变量赋值则是先声明变量,再对其进行赋值,初始化只能执行一次,赋值则可以执行多次。
:= 运算符左侧的变量应该是未声明过的,否则会导致编译错误执行输出结果
Go 语言的变量赋值与多数语言一致,但 Go 语言中提供了程序员期盼多年的多重赋值功能,比如下面这个交换 a 和 b 变量的语句:
执行输出结果
常量
常量一旦定义,就不能再被修改。比如:
5, -89, "I love Go", 67.89,这些都是常量字面量,它们不是常量标识符,而是直接写出来的固定值。关键字 const 被用于表示常量
结合 Go 语言变量定义方式,可以看到 Go 这种变量和常量的声明方式可读性很好,从左往右,第一个标识符 var 或 const 表明声明的是变量还是常量,第二个标识符标识变量或常量的内存存储块别名,以便后续引用,第三个标识符表示变量或常量的数据类型,可以省略,省略的情况下底层会在编译期自动推导对应的变量或常量类型。
预定义常量,Go 语言预定义了这些常量:true、false 和 iota。iota 比较特殊,可以被认为是一个可被编译器修改的常量,在每一个 const 关键字出现时被重置为 0,然后在下一个 const 出现之前,每出现一次 iota,其所代表的数字会自动增 1。可以简单理解为行索引,从第一行值是0,以此类推
输出结果
以下是错误写法,数组的容器内容可以发生改变,不能在编译期间明确下来。Go语言常量的定义,必须是能在编译器就要完全确定其值,所以值只能使用
字面常量运算符
在 Go 语言中,运算符(Operators)是用于在程序运行过程中执行各种数学计算、逻辑判断或位操作的符号。Go 语言内置了多种运算符,主要包括以下几类:
算术运算符:用于基本的数学运算,如加减乘除等。
赋值运算符:用于给变量赋值或结合操作与赋值,如 =, +=, -=, *=, /=, %= 等。
关系运算符:用于比较两个值之间的大小、是否相等等,如 ==, !=, >, <, >=, <=。
逻辑运算符:用于布尔逻辑判断,如 &&(与)、||(或)、!(非)。
位运算符:用于对整数类型执行按位操作,如 &, |, ^, <<, >> 等。
指针运算符:Go 虽然不像 C 那样允许指针运算,但也支持
&(取地址)和 *(根据地址取值)等基本指针操作。算术运算符
Go 支持标准的整数加减乘除及取余运算,示例参考:
Go 是一门强类型语言,不同类型的整数不能直接进行运算,比如 int32 + int 会导致编译错误,必须显式转换类型,例如:
Go 中的字面常量(无类型常量)在表达式中可以自动隐式转换为需要的类型,比如像 1 + 0.1 这样的混合计算是合法的。反之,如果参与运算的是有类型的变量,则必须显式转换才能进行计算。
1 和 0.1 都是 无类型(untyped)常量,也叫字面常量。这里 1 + 0.1 会被隐式转换为 float64(1) + float64(0.1),结果为 1.1。Go 中的常量如果没有明确指定类型(如 const a = 1.23),它就是无类型的,直到它被用在一个需要具体类型的地方,才会被自动转换。
赋值运算符
常见的赋值运算符:
=:简单的赋值运算符,将一个表达式的值赋给一个左值
+=:相加后再赋值
-=:相减后再赋值
*=:相乘后再赋值
/=:相除后再赋值
%=:求余后再赋值
&=:按位与后赋值
|=:按位或后赋值
^=:按位异或后赋值参考案例:
关系运算符
比较运算符运行的结果是布尔值。Go 是强类型语言,不同类型的值不能放在一起比较,否则在编译期就会报错。Go 语言支持以下几种常见的比较运算符:
==:检查两个值是否相等,如果相等返回 True 否则返回 False。
!=:检查两个值是否不相等,如果不相等返回 True 否则返回 False。
>:检查左边值是否大于右边值,如果是返回 True 否则返回 False。
>=:检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
<:检查左边值是否小于右边值,如果是返回 True 否则返回 False。
<=:检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。参考案例:
逻辑运算符
&&:逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。
||:逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。
!:逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。参考案例:
位运算符
位运算符以二进制的方式对数值进行运算,效率更高,性能更好,Go 语言支持以下这几种位运算符:
&:参与位与运算的两个数对应的二进制位都为1时才为真
|:参与位或运算的两个数对应的二进制位有任意一个为1则为真
^:参与位异或运算(^)的两个数对应的二进制位不一样则为真
<<:左移n位就是乘以2的n次方。 "a<<b"是把a的各二进位全部左移b位,高位丢弃,低位补0
>>:右移n位就是除以2的n次方。 "a>>b"是把a的各二进位全部右移b位,低位丢弃,高位补0参考案例:
指针运算符
在 Go 语言中,变量的本质是对一块内存空间的命名。我们可以通过变量名来访问这块内存中存储的值。而指针(pointer)是一种特殊的变量,它存储的不是具体的数值,而是另一个变量的内存地址。换句话说,指针变量的值表示的是某个变量在内存中的位置。
如图所示,变量 b 的值为 156,而 b 的内存地址为 0x1040a124。变量 a 存储了 b 的地址。我们就称 a 指向了 b。

我们来看一个简单的示例:
上面代码中的 ptr 就是一个指针类型,表示指向存储 int 类型值的指针。ptr 本身是一个内存地址值,所以需要通过内存地址进行赋值。通过
&a 可以获取变量 a 所在的内存地址,赋值之后,可以通过 *ptr 获取指针指向内存地址存储的变量值。我们把 a 的地址赋值给 *int 类型的 ptr。我们称 ptr 指向了 a。当我们打印 ptr 的值时,会打印出 a 的地址。程序将输出:在 Go 语言中,指针是一种保存变量内存地址的数据类型,可以通过指针修改其所指向内存地址中的值。当指针被声明但尚未初始化时,它的零值是 nil,表示尚未指向任何内存地址。我们可以使用取地址符
& 来获取一个变量的内存地址,并将其赋值给一个指针变量,从而完成指针的初始化。此时,该指针就指向了这个变量所占用的内存空间。要访问或修改指针指向的变量值,可以使用间接引用符 *(也称为解引用)。通过 *指针变量 的方式,我们可以读写其所指向地址中的数据。代码示例
数据类型
整型
Go 支持的整型类型非常丰富,在 Go 语言中,整型分为
有符号整型 和 无符号整型。有符号整型表示的数值可以为负数、零和正数,而无符号整型只能为零和正数。有符号整型:
int:占用空间大小取决于操作系统CPU架构,表示 32 或 64 位整型。除非对整型的大小有特定的需求,否则你通常应该使用 int 表示整型。
int8:表示 8 位有符号整型,范围:-128~127
int16:表示 16 位有符号整型,范围:-32768~32767
int32:表示 32 位有符号整型,范围:-2147483648~2147483647
int64:表示 64 位有符号整型,范围:-9223372036854775808~9223372036854775807
无符号整型:
uint:根据不同的底层平台,表示 32 或 64 位无符号整型。
uint8:表示 8 位无符号整型,范围:0~255
uint16:表示 16 位无符号整型,范围:0~65535
uint32:表示 32 位无符号整型,范围:0~4294967295
uint64:表示 64 位无符号整型,范围:0~18446744073709551615
在 Go 语言中,当你使用自动推导语法创建一个整数变量时,默认的数据类型是 int。Go 中的 int 和 uint 是
平台相关的类型,它们的位数(bit)取决于当前操作系统的位数:在32为操作系统下int相当于int32,uint相当于uint32;在64位操作系统下int相当于int64,uint相当于uint64。如果程序需要跨平台,或者你明确知道需要多少位的整数,推荐使用 int32、int64 等明确位数的类型,这样更安全、可移植性也更强。Go 是一种强类型语言,即使底层位数相同,不同的整型类型也不能直接混用。例如:
如果一个整数变量被赋予超过其类型能表示的最大或最小值,就会发生数据溢出。这种情况不会报错,但结果是错误的。
浮点型
Go语言提供两种精度的浮点数,即 float32 和 float64,其中 float32 占用4个字节大小,而 float64 占用8个字节大小。这些浮点数类型的取值范围极限值可以在Golang的math包中找到。在 Go 语言里,定义一个浮点型变量的代码如下:
对于浮点类型需要被自动推导的变量,其类型将被自动设置为 float64,而不管赋值给它的数字是否是用 32 位长度表示的。因此,对于以上的例子,下面的赋值将导致编译错误:cannot use floatValue2 (type float64) as type float32 in assignment
必须使用这样的强制类型转换才可以:
布尔型
Go 语言中的布尔类型与其他主流编程语言差不多,布尔类型关键字为
bool,一个布尔型的值只有两种:true 和 false,它们代表现实中的是和否。它们的值会经常被用于一些判断中,比如 if 语句等。Go 是强类型语言,变量类型一旦确定,就不能将其它类型的值赋值给该变量,因此,布尔类型不能接受其它类型的赋值,也不支持自动或强制的类型转换。以下的示例是一些错误的用法,会导致编译错误:
不过通过表达式计算得到的布尔类型结果可以赋值给 Go 布尔类型变量:
字符
在 Go 语言中,没有专门的字符类型,如果要表示单个字符,通常使用 byte 或 rune 类型。Go 语言中的字符使用单引号包裹起来,两种常用类型:
byte 类型:本质是 uint8,用于表示一个 ASCII 字符,通常用于处理英文字符、原始二进制数据。
rune 类型:本质是 int32,用于表示一个 Unicode 字符,更适合处理多语言文本。
==
字符串
==
Array(数组)
在 Go 语言中,数组(array)是一种最基本的数据结构,用于存储固定长度的、类型相同的元素集合。数组的特点包括:
长度固定:在定义数组时必须指定长度,长度一旦确定就不能更改。
元素类型一致:数组中的所有元素必须是同一种数据类型,比如全是 int、string,也可以是结构体等自定义类型。
元素可修改:虽然数组的长度和类型不能变,但数组中的元素值是可以更改的。
连续内存:数组元素在内存中是连续存放的,有利于性能优化。数组的声明
在 Go 语言中,数组的类型表示形式为
[n]T,其中 n 表示数组的长度,即元素个数,必须是一个常量,且在定义时就要明确;T 表示数组中元素的类型,所有元素类型必须一致。以上结果按顺序输出为
此外,还可以通过这种语法糖省略数组长度的声明,这种情况下,Go 会在编译期自动计算出数组长度
数组长度在声明后就不可更改,在声明时可以指定数组长度为一个常量或者一个常量表达式,常量表达式是指在编译期即可计算结果的表达式
执行输出的结果为
[0 0 0]索引
在 Go 语言中,数组的每个元素在内存中是连续存放的,每个元素都通过一个
下标(Index)来访问。下标从 0 开始,因此第一个元素的下标是 0,第二个是 1,依此类推,最后一个元素的下标是 length - 1。可以通过 array[下标] 的形式快速访问或修改指定位置的元素执行结果是 a1 的值是 a,a2 的值是 e。访问数组元素时,下标必须在有效范围内,比如对于一个长度为 5 的数组,下标有效范围是 0~4,超出这个范围编译时会报索引越界异常。

长度和容量
在 Go 语言中,数组的大小是固定的,并且在定义时就必须指定。数组的长度是其类型的一部分,换句话说,
[3]int 和 [5]int 是两种不同的类型。可以使用内置函数 len() 来获取数组的长度,它返回的是数组中元素的总数,这个值在数组生命周期内是固定的。在 Go 语言中,容量cap表示底层数据结构最多可以容纳多少个元素。对于数组来说,由于数组在定义时长度是固定的,所占内存空间在编译时就已经确定,因此数组的长度和容量是相等的,即
len(array) == cap(array)多维数组
在 Go 语言中,也可以创建多维数组,支持任意维度的数组(如三维、四维),但维度越高,结构越复杂。常见的就是二维数组,类似于表格或矩阵的结构。多维数组的定义方式:
遍历数组
我们可以通过 for 循环遍历所有数组元素
Go 语言还提供了一个关键字 range,用于以更优雅的方式遍历数组中的元素
range 表达式返回两个值,第一个是数组下标索引值,第二个是索引对应数组元素值,如果我们不想获取索引值,可以使用 _ 下划线丢弃
如果只想获取索引值,可以这么做
Slice(切片)
在 Go 语言中,数组的长度是固定的,并且在编译时就已经确定,因此适用于数据规模已知的场景。由于数组一旦声明,其长度就不可更改,这意味着我们无法动态向数组中添加新元素。如果想添加,就必须先创建一个容量更大的新数组,再将原数组的元素复制过去,最后才能添加新数据。这个过程在数组规模较大时会带来性能上的影响。此外,数组是值类型,在作为参数传递给函数时,会发生值拷贝。也就是说,函数接收到的是数组的副本,而不是原始数组本身。因此在函数中对数组的修改,并不会影响原数组。这种拷贝行为在数组较大时,也会显著降低程序性能。
综合以上因素,我们迫切需要一个引用类型的、支持动态添加元素的新数组类型。为了克服数组在使用上的这些限制,Go 语言引入了切片(slice),切片(slice)是对数组的一种封装,可以理解为动态数组。
底层依赖数组:切片的底层结构其实就是一个数组,对数组做任意区间的截取(slice)就可以得到切片。
类型字面量中没有长度:切片的定义形式是 []T,只包含元素类型,不需要指定长度,区别于数组 [n]T。
长度可变:切片的长度可以根据实际添加的元素动态增长,可以随时动态扩充存储空间,但不会自动缩小。
同样元素类型:切片中所有元素的类型必须相同。切片一个最强大的功能就是支持对元素做动态增删操作。实际上,我们在 Go 语言中很少使用数组,大多数时候会使用切片取代它。

- 链接:www.qianshuai.cn/article/go/datatype
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。









