布尔类型
布尔类型用于表示真假,类型名为bool,只有两个值true和false,占用一个字节宽度,0为false
var (
isTrue bool = true
isFalse bool = false
)
逻辑运算
(1) 与&&
只有左右两边表达式结果都为true,运算结果才为true
fmt.Println(true && true) //true
fmt.Println(true && false) //false
fmt.Println(false && true) //false
fmt.Println(false && false) //false
(2) 或||
fmt.Println(true || true) //true
fmt.Println(true || false) //true
fmt.Println(false || true) //true
fmt.Println(false || false) //false
(3) 非!
fmt.Println(!true) //false
fmt.Println(!false) //true
关系运算
fmt.Println(true == true) //等于==
fmt.Println(true != true) //不等于!=
整数类型
Go语言提供了5种有符号、5种无符号、1种指针、1种单字节、1种单个unicode字符(unicode码点),共13种整数类型,零值均为0
类型名 | 字节宽度 | 说明&取值范围 |
---|---|---|
int | 与平台有关,32位系统4字节,64位系统8字节 | 有符号整型 |
uint | 与平台有关,32 位系统 4 字节,64 位系统 8 字节 | 无符号整形 |
int8 | 1 字节 | 用 8 位表示的有符号整型,取值范为:[-128, 127] |
int16 | 2字节 | 用 16 位表示的有符号整型,取值范围为:[-32768,32767] |
int32 | 4字节 | 用 32 位表示的有符号整型,取值范围为:[-2147483648,2147483647] |
int64 | 8字节 | 用 64 位表示的有符号整型,取值范围为:[-9223372036854775808,9223372036854775807] |
uint8 | 1 字节 | 用 8 位表示的有符号整型,取值范为:[0, 255] |
uint16 | 2字节 | 用 16 位表示的有符号整型,取值范围为:[0,65535] |
uint32 | 4字节 | 用 32 位表示的有符号整型,取值范围为:[0,4294967295] |
uint64 | 8字节 | 用 64 位表示的有符号整型,取值范围为:[0,18446744073709551615] |
rune | 4字节 | Unicode 码点,取值范围同int32 |
byte | 1 字节 | 字节类型,取值范围同 uint8 |
uintptr | 与平台有关,32 位系统 4 字节,64 位系统 8 字节 | 指针值的无符号整型 |
字面量:
十进制表示法: 以 10 为基数,采用 0-9 十个数字,逢 10 进位,如:10
八进制表示法:以 8 为基数,采用 0-7 八个数字,逢 8 进位,使用 0 开头表示为八进制表示,如:010
十六进制表示法:以 16 为基数,采用 0-9 十个数字和 A-F 六个字母,逢 16 进位,使用0X 开头表示为十六进制 ,如:0X10
// 分别打印十进制,八进制,十六进制的字面量
fmt.Println(10, 010, 0X10) //10 8 16
算术运算
+、-、*、/、%、++、--,注意:针对/除数不能为0,且结果依然为整数
var n1, n2, n3 int = 1, -8, -2
var n4 uint = 4
// 算术运算
fmt.Println(n1 + n2) //-7
fmt.Println(n1 - n2) //9
fmt.Println(n1 * n2) //-8
fmt.Println(n1 / n2) //0 /向下取整
fmt.Println(n1 % n2) //1
n3++
n4--
fmt.Println(n3, n4) //-1 3
关系运算
、>=、 <、 <=、 ==、 !=
fmt.Println(1 > 2)
fmt.Println(1 >= 2)
fmt.Println(1 < 2)
fmt.Println(1 <= 2)
fmt.Println(1 == 2)
fmt.Println(1 != 2)
位运算
&、|、^、<<、 >>、 &^
对于负整数在计算机中使用补码进行表示,对应正整数二进制表示取反+1
针对左、右移的右操作数必须为无符号整型
二进制表示法
原码 正数
反码
补码 负数
// 位运算
// 2 二进制=> 0010
// 7 二进制=> 0111
// -2 二进制=> 1110 负整数在计算机中使用补码进行表示,对应正整数二进制表示取反+1
fmt.Println(4 & 2) // 0100 & 0010 => 0000
fmt.Println(4 | 2) // 0100 | 0010 => 0110
fmt.Println(4 ^ 2) // 0100 ^ 0010 => 0110
fmt.Println(4 &^ 2) // 0100 &^ 0010 => 0100
fmt.Println(4 << 2) // 0100 <<移2位 => 0001 0000
fmt.Println(8 >> 2) // 1000 >>移2位 => 0010
赋值运算
=、+=、-+、*=、/=、%=、&=、|=、^=、<<=、>>=
// 赋值运算
var n1, n2 int = 1, 2
n1 += n2 // n1 = n1 + n2
fmt.Println(n1) //3
n1 <<= n2 // n1 = n1 << n2
fmt.Println(n1) //12
类型转换
Go不会自动对数据类型转换,因此左、右操作数类型必须一致或int莫个字面量,可通过类型名(数据)的语法将数据转换为对应类型。需要大转小需要注意值截断和值溢出问题
不建议这种类型转换写法:大转小,有符号转无符号,无符号转有符号
// 类型转换
var n1, n2 int = 1, 4
fmt.Printf("%T %T %T %T %T %T\n", n1, uint(n1), n2, int(n2), uint8(n2), int64(n2)) //int uint int int uint8 int64
fmt.Printf进行格式化参数输出, 占位符:
%T 变量名类型
%b 二进制
%c 字符
%d 十进制
%+d 表示对正整数带+符号
%nd 表示最小占位n个宽度且右对齐
%-nd 表示最小占位n个宽度且左对齐
%0nd 表示最小占位n个宽度且右对齐,空字符使用0填充
%o 八进制
%#o 带0的前缀
%x、%X 十六进制
%#x(%#X)带0x(0X)的前缀
%U Unicode码点
%#U 带字符的Unicode码点
%q 带单引号的字符
// fmt打印
var (
n1 int = 10
n2 int = -10
n3 byte = 'a'
n4 rune = '虎'
)
fmt.Printf("%d %b %o %x %X %#o %#x %#X\n", n1, n1, n1, n1, n1, n1, n1, n1,) //10 1010 12 a A 012 0xa 0XA
fmt.Printf("|%d|%+d|%10d|%-10d|%010d|%+-10d|%+010d|\n", n1, n1, n1, n1, n1, n1, n1) //10|+10| 10|10 |0000000010|+10 |+000000010
fmt.Printf("%d|%+d|%10d|%-10d|%010d|%+-10d|%+010d|\n", n2, n2, n2, n2, n2, n2, n2) //-10|-10| -10|-10 |-000000010|-10 |-000000010
fmt.Printf("%c %c %q %q\n", n3, n4, n3, n4) //a 虎 'a' '虎'
fmt.Printf("%U %#U\n", n4, n4) //U+864E U+864E '虎'
浮点型
浮点数用于表示带小数的数字,Go提供float32和float64两种浮点类型,非精确值类型,零值0.0
主要是为了表示小数,也可细分为float32和float64两种。浮点数能够表示的范围可以从很小到很巨大,这个极限值范围可以在math包中获取,math.MaxFloat32表示float32的最大值,大约是3.4e38,math.MaxFloat64大约是1.8e308,两个类型最小的非负值大约是1.4e-45和4.9e-324。
float32大约可以提供小数点后6位的精度,作为对比,float64可以提供小数点后15位的精度。通常情况应该优先选择float64,因此float32的精确度较低,在累积计算时误差扩散很快,而且float32能精确表达的最小正整数并不大,因为浮点数和整数的底层解释方式完全不同。
类型 | 长度 |
---|---|
float32 | IEEE-754 32位浮点型数 |
float64 | IEEE-754 64位浮点型数 |
字面量
十进制表示法:3.1415926
科学计数法:1e-5
格式
// 浮点型
var (
f1 float32 = 3.1415926
f2 float32 = 3E-3
f3 float64 = 3.1E10
)
算术运算
+、-、*、/、++、--
注意:针对/除数不能为0
// 浮点型-算术运算
var (
f1 float32 = 3.1415926
f2 float32 = 3E-3
)
fmt.Println(f2) //0.003
fmt.Println(f1 + f2) //3.1445925
fmt.Println(f1 - f2) //3.1385925
fmt.Println(f1 * f2) //0.009424778
fmt.Println(f1 / f2) //1047.1975
f1++
f2--
fmt.Println(f1, f2) //4.1415925 -0.997
关系运算
、>=、<、<=
浮动型不能进行==或者!=比较,可选择使用两个浮点数的差在一定区间内则认为相等
// 浮点型-关系运算
var (
f1 float32 = 3.1415926
f2 float32 = 3E-3
)
fmt.Println(f1 > f2)
fmt.Println(f1 >= f2)
fmt.Println(f1 < f2)
fmt.Println(f1 <= f2)
赋值运算
=、-=、*=、/=
var (
f1 float32 = 3.1415926
f2 float32 = 3E-3
)
f1 += f1
f2 -= f2
fmt.Println(f1)
fmt.Println(f2)
类型转换
Go不会自动对数据类型转换,因此左、右操作数类型必须一致或int莫个字面量,可通过类型名(数据)的语法将数据转换为对应类型。需要注意值截断和值溢出问题
var (
f1 float32 = 3.1415926
)
fmt.Printf("%T %T\n", f1, float64(f1))
fmt.Printf进行格式化参数输出,占位符:
%f、%F 十进制表示法
%n.mf 表示最小占n个宽度并且最多保留m位小数
%e、%E 科学计数法表示
%g、%G 自动选择最紧凑得表示方法%e\%E和%f\%F
// fmt.Printf打印
var (
f1 float32 = 3.1415926
f3 float32 = 3E-3
)
fmt.Printf("%f %f %F %F\n", f1, f3, f1, f3) //3.141593 0.003000 3.141593 0.003000
fmt.Printf("%e %e %E %E\n", f1, f3, f1, f3) //3.141593e+00 3.000000e-03 3.141593E+00 3.000000E-03
fmt.Printf("%g %g %G %G\n", f1, f3, f1, f3) //3.1415925 0.003 3.1415925 0.003
fmt.Printf("|%5.2f| |%5.2f|\n", f1, f3) //| 3.14| | 0.00|
复数型
Go 提供 complex64 和 complex128 两种复数类型,针对 complex64 复数的实部和虚部均使用
float32,针对 complex128 复数的实部和虚部均使用 float64
类型 | 长度 |
---|---|
complex64 | 32位实数和虚数 |
complex128 | 64位实数和虚数 |
字面量
十进制表示法:1 + 2i,i*i = -1; 1为实部,2为虚部
常用函数
complex: 工厂函数,通过两个参数创建一个复数
real: 用于获取复数的实部
imag:用于获取复数的虚部
// 复数
var (
c1 complex64 = 1 + 2i
c2 complex64 = complex(3, 4)
)
fmt.Println(c1 + c2) //(4+6i)
fmt.Println(real(c1), imag(c1)) //1 2
字符串类型
Go语言内置了字符串类型,使用类型名string表示,零为空字符串""
只读的Unicode字节序列,Go语言使用UTF-8格式编码Unicode字符,每个字符对应一个rune类型。一旦字符串变量赋值之后,内部的字符就不能修改,英文是一个字节,中文是三个字节。
字面量
可解析字符串:通过双引号"来创建,不包含多行,支持特殊字符转义序列
原生字符串:通过反引号`来创建,包含多行,不支持特殊字符转义序列
特殊字符
\\ 反斜杠
\' 单引号
\" 双引号
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
\ooo 3个8位数字给定的八进制码点的Unicode字符(不能超过\377)
\uhhhh 4个16位数字给定的十六进制码点的Unicode字符
\Uhhhhhhhh 8个32位数字给定的十六进制码点的Unicode字符
\xhh 2个8位数字给定的十六进制码点的Unicode字符
// 字符串
var (
s1 string = "aaa\nbbb"
s2 string = `aaa\nbbb`
s3 string = `aaa
bbb`
s4 string = "qwertyuiopasdfghjklzxcvbnm"
s5 string = "我爱中国"
)
fmt.Println(s1)
fmt.Println(s2) //aaa\nbbb
fmt.Println(s3) /*aaa
bbb*/
fmt.Println(s4)
fmt.Println(s5) //我爱中国
常用操作
字符串拼接 +
关系运算符 >、>=、<、<=、==、!=
赋值运算符 +=
索引 s[index],针对只包含ascii字符的字符串
切片 s[start:end],针对只包含ascii字符的字符串
// 字符串
var (
s1 string = "aaa\nbbb"
s2 string = `aaa\nbbb`
s3 string = `aaa
bbb`
s4 string = "qwertyuiopasdfghjklzxcvbnm"
s5 string = "我爱中国"
)
fmt.Println(s4+"aaaaaaaaaaaaaaaasssdscas") //qwertyuiopasdfghjklzxcvbnmaaaaaaaaaaaaaaaasssdscas
s5 += "--来自南方"
fmt.Println(s5) //我爱中国--来自南方
fmt.Println(s1 == s2) //false
fmt.Println(s1 != s3) //true
fmt.Println("abc" > "abe") //false
fmt.Println("abc" >= "abe") //false
fmt.Println("abc" <= "abe") //true
fmt.Println(s4[0]) //索引0的ascii:113
// 切片前包后不包
fmt.Println(s4[:], s4[3:], s4[:8], s4[3:8]) //qwertyuiopasdfghjklzxcvbnm rtyuiopasdfghjklzxcvbnm qwertyui rtyui
常用函数
len 获取字符串长度(针对只包含ascii字符的字符串)
string 将byte或rune数组转换为字符串
使用fmt.Printf进行格式化参数输出,占位符:
%s 基本的字符串输出
%d 十进制
// 字符串
var (
s1 string = "aaa\nbbb"
s5 string = "我爱中国"
)
fmt.Printf("%s len %d\n", s1, len(s1))
fmt.Printf("%s len %d\n", s5, len(s5))
类型转换
string转int: int, err := strconv.Atoi(string)
string转int64: int64, err := strconv.ParseInt(string, 10, 64)
int转string: string := strconv.Itoa(int)
int64转string: string := strconv.FormatInt(int64, 10)
枚举类型
常使用iota生成器用于初始化一系列相同规则的常量,批量声明常量的第一个常量的使用iota进行赋值,此时 iota 被重置为 0,其他常量省略类型和赋值,在每初始化一个常量则加 1
const (
i1 int = iota
i2
i3 int = iota
i4
)
func main() {
// 枚举类型
const (
i5 = iota
i6
i7 = 7
i8 = iota
i9
)
fmt.Println(i1, i2, i3, i4, i5, i6, i7, i8, i9) //0 1 2 3 0 1 7 3 4
}
指针类型(Pointer)
一个指针变量可以指向任何一个值的内存地址。它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。当然,可以声明指针指向任何类型的值来表明它的原始性或结构性;你可以在指针类型前面加上号(前缀)来获取指针所指向的内容,这里的号是一个类型更改器。使用一个指针引用一个值被称为间接引用。
当一个指针被定义后没有分配到任何变量时,它的值为 nil,表示空指针
一个指针变量通常缩写为 ptr。
符号 ““ 可以放在一个指针前,如 “intP”,那么它将得到这个指针指向地址上所存储的值;这被称为反引用(或者内容或者间接引用)操作符;另一种说法是指针转移。
对于任何一个变量 var, 如下表达式都是正确的:var == *(&var)
注意事项:
你不能得到一个数字或常量的地址,下面的写法是错误的。
//例如:
const i = 5
ptr1 := &i // error: cannot take the address of i
ptr2 := &10 // error: cannot take the address of 10
所以说,Go 语言和 C、C++ 以及 D 语言这些低级(系统)语言一样,都有指针的概念。
但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算(所谓的指针算法,如:pointer+2,移动指针指向字符串的字节数或数组的某个位置)是不被允许的。
Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。
因此 c = *p++ 在 Go 语言的代码中是不合法的。
指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。
另一方面(虽然不太可能),由于一个指针导致的间接引用(一个进程执行了另一个地址),指针的过度频繁使用也会导致性能下降。
指针也可以指向另一个指针,并且可以进行任意深度的嵌套,导致你可以有多级的间接引用,但在大多数情况这会使你的代码结构不清晰。
如我们所见,在大多数情况下 Go 语言可以使程序员轻松创建指针,并且隐藏间接引用,如:自动反向引用。
对一个空指针的反向引用是不合法的,并且会使程序崩溃:
package main
func main() {
var p *int = nil
*p = 0
}
// 结果err: panic: runtime error: invalid memory address or nil pointer dereference
// 紧急:运行时错误:无效的内存地址或nil指针取消引用
指针的使用方法:
- 定义指针变量;
- 为指针变量赋值;
- 访问指针变量中指向地址的值;
- 在指针类型前面加上*号来获取指针所指向的内容。
package main
import "fmt"
func main() {
var a, b int = 20, 30 // 声明实际变量
var ptra *int // 声明指针变量
var ptrb *int = &b
ptra = &a // 指针变量的存储地址 // 取出a的地址赋值给ptra => 取引用
fmt.Printf("a 变量的地址是: %x\n", &a)
fmt.Printf("b 变量的地址是: %x\n", &b)
// 指针变量的存储地址
fmt.Printf("ptra 变量的存储地址: %x\n", ptra)
fmt.Printf("ptrb 变量的存储地址: %x\n", ptrb)
// 使用指针访问值
fmt.Printf("*ptra 变量的值: %d\n", *ptra) // 获取ptra内存地址中对应的存储的值 => 解引用
fmt.Printf("*ptrb 变量的值: %d\n", *ptrb) // 获取ptrb内存地址中对应的存储的值 => 解引用
}
数组值类型
package main
import "fmt"
func main() {
nums := [5]int{1, 2, 3, 4, 5}
nums2 := nums
fmt.Println(nums, nums2)
nums2[0] = 100
fmt.Println(nums, nums2) // nums的[0]不受影响
// 指针
// 值类型
// int, float, bool, string => 值类型
// 赋值 原有的数据复制一份给新的变量
// 两个变量之间没有任何联系
// 对nums进行任何修改都不会影响nums2
// 对nums2进行任何修改也不会影响nums
var numsPoint *[5]int = &nums // 取出nums数组地址赋值给numsPoint => 取引用
fmt.Printf("%T\n", numsPoint)
numsPoint[0] = 100
fmt.Println(nums, numsPoint) // 指针影响
}