Go语言是静态类型语言,因此变量(variable)是有明确类型的,编译器也会检查变量类型的正确性。在数学概念中,变量表示没有固定值且可改变的数。但从计算机系统实现角度来看,变量是一段或多段用来存储数据的内存。
Go语言在声明变量时,自动对变量对应的内存区域进行初始化操作。每个变量会初始化其类型的默认值,例如:
0
和 0.0
。""
。 false
。nil
。变量一旦声明,就必须使用,不然编译报错!
Go虽然支持自动类型推导,但仍然是强类型语言!
// var 是声明变量的关键字,name 是变量名,type 是变量的类型
var name type
// 批量声明并赋值
var a,b,c int = 10,20,30
// 自动类型推导并批量声明变量然后赋值
var a,b,c = 10,20.99,true
// 批量声明一组数据
var (
a int
b string
c []float32
)
// 自动类型推导并批量声明一组变量然后赋值
var (
d = 10
e = 20
)
// 此时就是声明变量并赋值,而且自动类型推导
a := 10
func main(){
s := 32
a,b := 11,"world"
}
简短模式(short variable declaration)有以下限制:
:
和=
中间不能有空格因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化。
var 形式的声明语句往往是用于需要显式指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
匿名变量的特点是一个下划线_
,_
本身就是一个特殊的标识符,被称为空白标识符。
可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。
匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
package main
import (
"fmt"
)
func main() {
//只需要x坐标,所以y坐标就可以使用匿名变量来接收
x, _ := getPoint()
fmt.Println(x)
}
func getPoint() (int, int) {
return 10, 200
}
Go语言中的常量使用关键字const
定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此。由于编译时的限制,定义常量的表达式必须为能被编译器求值的常量表达式。
注意:常量的数据类型只可以是布尔、数字(整数、浮点和复数)、字符串、枚举
常量声明后,并不要求必须使用
const a [type] = value
const a,b,c [type] = value1,value2,value3
//常量也支持自动类型推导
const a = value
//常量也支持批量声明
const a,b,c = value1,value2,value3
const (
a = 10
b int = 20
c = "hello"
)
// 批量声明相同值常量,此时a、b、c的值都是10
const (
a = 10
b
c
)
注意:常量的值必须是能够在编译时就能够确定的,可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。
const a int = 10 + 2 //正确
const a int = getValue() //错误
常量声明可以使用 iota 常量生成器初始化(作为枚举),它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在每一个const关键字出现时,都会被重新重置为0,然后每出现一个常量,iota所代表的数值会自动增加1。
const (
a int = iota //0
b //1
c //2
)
可以定义类似java的枚举类型
//将 int 定义为 Weekday 类型
type Weekday int
const (
Sunday Weekday = iota //0
Monday //1
Tuesday //2
Wednesday //3
Thursday //4
Friday //5
Saturday //6
)
const (
a int = iota + 10 //10
b //11
c //12
)
使用下划线进行跳过
const (
a int = iota //0
_
_
b //3
c //4
)
const (
a int = iota //0
b int = 35 //35
c int = iota //2
)
bool
true
或者 false
,不可参与数值运算,也无法进行类型转换。int
、uint
、int8
、int16
、int32
、int64
、uint8
、uint16
、uint32
、uint64
、byte(uint8)
、rune(int32)
、float32
、float64
、complex128
、complex256
byte
代表一个字节,也是ASCII的一个字符;rune
代表Unicode的一个字符string
*int
[10]int
struct
chan int
func
[]int
interface
map
Go语言的数值类型分为以下几种:整数、浮点数、复数,其中每一种都包含了不同大小的数值类型,例如有符号整数包含 int8、int16、int32、int64 等,每种数值类型都决定了对应的大小范围和是否支持正负符号。
math
包提供了方法math.Max[Type]
和math.Min[Type]
查看各类型数据的最大最小值
例如int8
代表该类型变量占8Bit(位),也就是1Byte(字节),可以使用unsafe.Sizeof(variable)
查看变量所占Byte(字节)大小
有符号类型 | 说明 | 无符号类型 | 说明 | 占用字节大小 |
---|---|---|---|---|
int8 | 有符号 8 位整型 (-128 到 127) | uint8 | 无符号 8 位整型 (0 到 255) | 1 |
int16 | 有符号 16 位整型 (-32768 到 32767) | uint16 | 无符号 16 位整型 (0 到 65535) | 2 |
int32 | 有符号 32 位整型 (-2147483648 到 2147483647) | uint32 | 无符号 32 位整型 (0 到 4294967295) | 4 |
int64 | 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) | uint64 | 无符号 64 位整型 (0 到 18446744073709551615) | 8 |
int | 有符号整型,由cpu决定,长度在32bit 或 64bit 之间变化 | uint | 无符号整型,由cpu决定,长度在32bit 或 64bit 之间变化 | 4/8 |
大多数情况下,我们只需要 int 一种整型即可,它可以用于循环计数器(for 循环中控制循环次数的变量)、数组和切片的索引,以及任何通用目的的整型运算符,通常 int 类型的处理速度也是最快的。
除此之外,go还支持一些其他整形
类型 | 说明 |
---|---|
byte | 和 uint8 是等价类型type byte = uint8 ,byte 类型一般用于强调数值是一个原始的数据(一个字节,也可以代表ASCII 码的一个字符)而不是一个小的整数 |
rune | 和 int32 类型是等价的type rune = int32 ,通常用于表示一个 Unicode 码点(字符) |
uintptr | 没有指定具体的 bit 大小但是足以容纳指针,uintptr 类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方 |
Go语言提供了两种精度的浮点数 float32 和 float64,它们的算术规范由 IEEE754 浮点数国际标准定义,该浮点数规范被所有现代的 CPU 支持。
这些浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 math 包中找到:
math.MaxFloat32
表示 float32 能取到的最大数值,大约是 3.4e38
;math.MaxFloat64
表示 float64 能取到的最大数值,大约是 1.8e308
;1.4e-45
和4.9e-324
。一个 float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度,通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。
如果是64位操作系统,则浮点数自动类型推导默认数据类型为float64
a := 1129.6
fmt.Println(a * 100) // 112959.99999999999
m1 := 8.2
m2 := 3.8
fmt.Println(m1 - m2) // 4.3999999999999995
使用第三方包https://github.com/shopspring/decimal
进行解决
go get -u github.com/shopspring/decimal
a := decimal.NewFromFloat(1129.6)
i := decimal.NewFromInt(100)
r1 := a.Mul(i)
fmt.Println(r1) // 112960
m1 := decimal.NewFromFloat(8.2)
m2 := decimal.NewFromFloat(3.8)
r2 := m1.Sub(m2)
fmt.Println(r2) // 4.4
a.Add(b)
:加法,a + b
a.Sub(b)
:减法,a - b
a.Mul(b)
:乘法,a * b
a.Div(b)
:除法,a / b
a.Cmp(b)
比较大小,a > b
返回值> 0
;a = b
返回值= 0
;a < b
返回值< 0
Go语言中复数的类型有两种,分别是 complex128(64 位实数和虚数)和 complex64(32 位实数和虚数),其中 complex128 为复数的默认类型。
复数的值由三部分组成 RE + IMi
,其中RE
是实数部分,IM
是虚数部分,RE
和IM
均为 float 类型,而最后的i
是虚数单位。
声明格式:
/*
name 为复数的变量名,complex128 为复数的类型,“=”后面的 complex 为Go语言的内置函数用于为复数赋值,x、y 分别表示构成该复数的两个 float64 类型的数值,x 为实部,y 为虚部
*/
var name complex128 = complex(x, y)
//获取实部
real(name)
//获取虚部
imag(name)
一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是 UTF-8 字符的一个序列(当字符为 ASCII 码表上的字符时则占用 1 个字节,其它字符根据需要占用 2-4 个字节)。
UTF-8 是一种被广泛使用的编码格式,由于该编码对占用字节长度的不定性,在Go语言中字符串也可能根据需要占用 1 至 4 个字节
字符串是一种值类型,且值不可变,即创建某个文本后将无法再次修改这个文本的内容,在golang中,字符串是字节的定长切片(byte slice),如果真要修改字符串中的字符,将 string
转为[]byte
修改后,再转为 string
即可。
字符串所占的字节长度可以通过函数 len()
来获取
package main
import (
"fmt"
)
func main() {
x := "abcde"
fmt.Println(len(x)) //5
fmt.Println(x[0]) //97
}
注意:方法len()
计算的是字节长度,由于ASCII码中1字符等于1字节,可以等价字符长度。如果是Unicode字符,比如字符串"你好"
,使用len()
计算所占用的字节长度就是6,而不是2。
因为在Go里面,汉字使用的是UTF-8编码,每个汉字占用3个字节,每个数字、字母、符号占用1个字节。
如果需要计算Unicode字符长度,那么就需要使用utf8
包下的RuneCountInString()
函数
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
x := "你好"
fmt.Println(len(x)) //6
fmt.Println(utf8.RuneCountInString(x)) //2
}
也可以将字符串转为rune数组
package main
import (
"fmt"
)
func main() {
a := "你好go"
runes := []rune(a)
fmt.Println(len(runes))
}
package main
import (
"fmt"
)
func main() {
//使用反引号进行定义
x := `abcdefg
hijklmn
opqrst
uvwxyz
`
fmt.Println(x)
}
对于ASCII编码,直接使用下标进行循环遍历
package main
import (
"fmt"
)
func main() {
x := "abcde"
for i := 0; i < len(x); i++ {
fmt.Printf("%c\n", x[i])
}
}
对于Unicode编码,需要使用for...range...
,或者转为rune数组后遍历
package main
import (
"fmt"
)
func main() {
x := "你好"
for _, c := range x {
fmt.Printf("%c\n", c)
}
}
注意:字符串是不可变的字符序列,即使修改后,也是重新分配了内存空间,生成了新的字符串!
s := "big"
sb := []byte(s)
sb[0] = 'p'
s = string(sb)
fmt.Println(s)
z := "白萝卜"
zr := []rune(z)
zr[0] = '红'
z = string(zr)
fmt.Println(z)
Go语言拼接字符串有五种方法,分别是如下:
使用+
号拼接,要求+
号两边都是string类型
a := "hello"
b := " world"
c := a + b
使用fmt.Sprint()
拼接,可以实现任意数据类型的拼接
a := "hello"
b := 1234
c := fmt.Sprint(a, b)
使用strings.Join
函数拼接
strings.Join([]string{"hello", "world"}, "-")
使用bytes.Buffer
,性能比上面的方式好
a := "hello"
b := "world"
var bt bytes.Buffer
bt.WriteString(a)
bt.WriteString(b)
c := bt.String()
使用strings.Builder
,性能比bytes.Buffer
还要好,官方推荐该方法
a := "hello"
b := "world"
var sb strings.Builder
sb.WriteString(a)
sb.WriteString(b)
c := sb.String()
Go语言中字符的声明方式如下:
var c1 byte = 'x'
var c2 rune = '你'
c3 := '好'
fmt.Printf("%c,%c,%c\n", c1, c2, c3)
Go语言的字符有以下两种:
type byte = uint8
:代表了 ASCII 码的一个字符。type rune = int32
:代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。//十进制字符
var ch1 byte = 65
fmt.Printf("%c\n", ch1) //A
//两位十六进制,使用单引号,并且加\x前缀
var ch2 byte = '\x41'
fmt.Printf("%c\n", ch2) //A
//三位八进制,使用单引号,并且加\前缀
var ch3 byte = '\101'
fmt.Printf("%c\n", ch3) //A
Unicode(Utf8)编码,在文档中,一般使用格式 U+hhhh
来表示,其中 h 表示一个 16 进制数
在书写 Unicode 字符时,需要在 16 进制数之前加上前缀\u
或者\U
。因为 Unicode 至少占用 2 个字节,所以我们使用 int16 或者 int 类型来表示。如果需要使用到 4 字节,则使用\u
前缀,如果需要使用到 8 个字节,则使用\U
前缀。
var ch1 int16 = '\u4f60' //你
var ch2 rune = '\u597d' //好
fmt.Printf("%c%c\n", ch1, ch2)
在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明:
package main
import (
"fmt"
)
func main() {
var a int = 10
var b byte = byte(a)
fmt.Println(b) // 10
s := "hello"
sb := []byte(s)
fmt.Println(sb) // [104 101 108 108 111]
}
注意: 类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将 int16 转换为 int32)。当从一个取值范围较大的类型转换到取值范围较小的类型时(将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。
注意: Go中没有自动类型提升,例如int和float相加,需要先把int类型显式的转换为float类型,然后再相加。
//类型定义
type NewType Type
//类型别名
type TypeAlias = Type
区别
//类型定义
type NewInt int
//类型别名
type MyInt = int
func main() {
var a NewInt
var b MyInt
fmt.Printf("type of a:%T\n", a) // type of a:main.NewInt
fmt.Printf("type of b:%T\n", b) // type of b:int
}
a
的类型是main.NewInt
,表示main
包下定义的NewInt
类型。
b
的类型是int
。MyInt
类型只会在代码中存在,编译完成时并不会有MyInt
类型。
总结: 类型定义type NewType Type
是定义了一个新的类型;类型别名type TypeAlias = Type
只是给原来的类型起了个别名,在编译时还是以原来的类型为准
运算符 | 描述 | 实例(A=10;B=20) |
---|---|---|
+ | 相加 | A + B 输出结果 30 |
- | 相减 | A - B 输出结果 -10 |
* | 相乘 | A * B 输出结果 200 |
/ | 相除 | B / A 输出结果 2 |
% | 求余 | B % A 输出结果 0 |
++ | 自增 | A++ 输出结果 11 |
-- | 自减 | A-- 输出结果 9 |
注意: golang中的++
和--
,只可以写在变量后面;并且是作为单独语句出现而不是表达式,所以不可以运算后赋值
运算符 | 描述 | 实例(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 |
运算符 | 描述 | 实例(A=true;B=false) |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | A && B 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | `A |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !B 为 True |
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | 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 |
<<= | 左移后赋值 | C <<= 2 等效于 C = C << 2 |
>>= | 右移后赋值 | C >>= 2 等效于 C = C >> 2 |
&= | 按位与后赋值 | C &= 2 等效于 C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 等效于 C = C ^ 2 |
|= | 按位或后赋值 | `C |
位运算符用于整数在内存中的二进制位进行操作
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) |
---|---|---|
& | 按位与。 其功能是参与运算的两数各对应的二进位相与。 | 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 |
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a :获取变量的实际地址。 |
* | 指针变量。 | *a :对指针变量a,解引用 |
在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串""
,而指针、切片、映射、通道、函数和接口的零值则是nil
。
nil == nil
是错误的nil
,但是最好不要这么做fmt.Printf("%T",nil)
,来获取类型0x0
,可以使用fmt.Printf("%p",variables)
查看指针==
进行比较reflect.DeepEqual()
函数