Go语言基础——02 数据类型、运算符、控制结构
数据类型
- 基本数据类型
- 布尔类型
- 数字类型
- 整型
- 浮点型
- 字符串类型
- 派生数据类型
- 指针
- 数组
- 结构体
- 通道(channel)
- 切片(slice)
- 函数
- 接口(interface)
- Map
布尔类型
bool 型的值只能是常量:true 和 false。
两个相同类型的值可以通过运算符来进行比较,获得一个布尔型的值。
package main
import "fmt"
func main() {
var a bool = true
var b bool = false
fmt.Println(a, b) // true false
var c int = 10
fmt.Println(c < 5) // false
}
数字类型
整型
- 无符号整数
- uint8(0 —> 255)
- uint16(0 —> 65535)
- uint32(0 —> 4294967295)
- uint64(0 —> 18446744073709551615)
- 整数
- int8(-128 —> 127)
- int16(-32768 —> 32768 )
- int32(-2147483648 —> 2147483647)
- int64(-9223372036854775808 —> 9223372036854775807)
浮点型
- float32(IEEE-754 32位浮点型数;± 1e-45 —> ± 3.4 * 1e38)
- float64(IEEE-754 64位浮点型数;± 5 1e-324 —> 107 1e308)
- complex64(32位实数和虚数)
- complex128(64位实数和虚数)
其它数字类型
- byte(类似 uint8)
- rune(类似 int32)
- uint(32 或 64位)
- int(与 uint 一样大小)
- uintptr(无符号整型,用于存放一个指针)
package main
import "fmt"
func main() {
var a int32
var b int64
a = 15
//b = a + b // 编译错误 Cannot use 'a + b' (type int32) as the type int64
b = b + 5 // 5
fmt.Println(a, b)
//var c int8 = 128 // 编译错误 128' (type untyped int) cannot be represented by the type int8
//var d uint8 = -1 // 编译错误 The value of '-1' (type untyped int) cannot be represented by the type uint8
var e float32 = 1.22323323
fmt.Println(e) // 1.2232332
var f float64 = 1.223233231231234
fmt.Println(f) // 1.223233231231234
fmt.Printf("f=%.2f\n", f) // f=1.22
var g complex64 = complex(1, 2)
fmt.Println(g) // (1+2i)
fmt.Println(077) // 63
fmt.Println(0xFF) // 255
fmt.Println(1e3) //1000
// 类型转换
j := int64(a)
fmt.Printf("j=%d, j的类型:%T\n", j, j) // j=15, j的类型:int64
}
注意:
- 整型的零值为 0,浮点型的零值为 0.0。
- int 型是计算最快的一种类型。
- float32 精确到小数点后7位,float64 精确到小数点后15位。
- 可以通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示16 进制数(如:0xFF),使用 e 来表示 10 的连乘(如:1e3 = 1000,或 6.022e23 = 6.022 x 1e23)。
字符串类型
字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
编码表
UTF-8编码:目前兼容我们绝大部分的文字和符号。
GBK编码:中国的编码表,号称兼容所有的中文汉字。
Unicode编码表:号称兼容了全世界的文字。
package main
import "fmt"
func main() {
var str string = "这是一个字符串"
fmt.Printf("str的类型:%T,str=%s\n", str, str) // str的类型:string,str=这是一个字符串
str1 := 'A'
str2 := "A"
fmt.Printf("str1的类型:%T,str1=%d\n", str1, str1) // str1的类型:int32,str1=65
fmt.Printf("str2的类型:%T,str2=%s\n", str2, str2) // str2的类型:string,str2=A
str3 := '你'
fmt.Printf("str3的类型:%T,str3=%d\n", str3, str3) // str3的类型:int32,str3=20320
}
- Go语言中,所有的字符串都是由 单个字符 连接起来的
- 单引号表示字符,双引号表示字符串(string)类型
- 字符的本质是整型
转义字符
转义字符,所有的ASCII码
都可以用"\"+数字
(一般是8进制数字)来表示。
/*
换行
你
好
*/
fmt.Println("你\n好")
/*
删除上一个字符
好
*/
fmt.Println("你\b好")
/*
tab
你 好
*/
fmt.Println("你\t好")
数据类型转换
- 在必要且可行的情况下,一个类型的值可以被转换为另一个类型的值。
- Go语言中不存在隐式类型转换。所有的类型转换都必须显示的声明:新类型的值 = 新类型(旧类型的值)
package main
import "fmt"
func main() {
var a float64 = 2.32
var b int
b = int(a)
fmt.Printf("%T, %.2f\n", a, a) // float64, 2.32
fmt.Printf("%T, %d\n", b, b) // int, 2
var c int = 12
var d float64
d = float64(c)
fmt.Printf("%T, %d\n", c, c) // int, 12
fmt.Printf("%T, %.2f\n", d, d) // float64, 12.00
//var e bool = true
//var f int64
// 编译错误 Cannot convert an expression of the type 'bool' to the type 'int64'
//f = int64(e)
}
注意:
- 布尔类型不支持类型转换
- 浮点数转整数,会被截断,只保留整数部分
Go语言运算符
运算符用于执行程序代码运算。
Go语言内置的运算符有:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
每个运算符都有它们的优先级,计算机根据其优先级的不同,计算顺序也有所不同。
以下列出所有运算符以及它们的优先级,由上至下代表优先级由高到低:
优先级 运算符
7 ^ !
6 * / % << >> & &^
5 + - | ^
4 == != < <= >= >
3 <-
2 &&
1 ||
注意:可以通过括号来临时提升某个表达式的整体运算优先级
算术运算符
运算符 描述
+ 相加
- 相减
* 相乘
/ 相除
% 求余
++ 自增
-- 自减
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
// 相加:10 + 20 = 30
fmt.Printf("%d + %d = %d\n", a, b, a+b)
// 相减:10 - 20 = -10
fmt.Printf("%d - %d = %d\n", a, b, a-b)
// 相乘:10 * 20 = 200
fmt.Printf("%d * %d = %d\n", a, b, a*b)
// 相除:20 / 10 = 2
fmt.Printf("%d / %d = %d\n", b, a, b/a)
// 求余:20 % 10 = 0
fmt.Printf("%d %% %d = %d\n", b, a, b%a)
// 自增:a++ = 11
a++
fmt.Printf("a++ = %d\n", a)
// 自减:a-- = 10
a--
fmt.Printf("a-- = %d\n", a)
}
关系运算符
运算符 描述
== 检查两个值是否相等,如果相等返回 true 否则返回 false
!= 检查两个值是否不相等,如果不相等返回 true 否则返回 false
> 检查左边的值是否大于右边的值,如果是返回 true 否则返回 false
< 检查左边的值是否小于右边的值,如果是返回 true 否则返回 false
>= 检查左边的值是否大于等于右边的值,如果是返回 true 否则返回 false
<= 检查左边的值是否小于等于右边的值,如果是返回 true 否则返回 false
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
// ==:false
fmt.Println(a == b)
// !=:true
fmt.Println(a != b)
// >:false
fmt.Println(a > b)
// <:true
fmt.Println(a < b)
// >=:false
fmt.Println(a >= b)
// <=:true
fmt.Println(a <= b)
}
逻辑运算符
运算符 描述
&& 逻辑与(和)AND,只要有一个为false,则为false;都为true,才为true
|| 逻辑或(or)OR,只要有一个为true,则为true;都为false,才为false
! 取反,如果是真,则为假;如果是假,则为真
package main
import "fmt"
func main() {
var a bool = true
var b bool = false
var c bool = true
var d bool = false
// &&:如果都为真,则为真;否则为假
fmt.Println(a && b) // true && false = false
fmt.Println(a && c) // true && true = true
fmt.Println(b && d) // false && false = false
// ||:如果其中一个为真,则为真;否则为假
fmt.Println(a || b) // true && false = true
fmt.Println(a || c) // true && true = true
fmt.Println(b || d) // false && false = false
// !:如果是真,则为假;否则为真
fmt.Println(!a) // !true = false
fmt.Println(!b) // !false = true
}
位运算符
运算符 描述
& 按位与(&),参与运算的两数各对应的二进位相与。都是1 结果为 1,否则为 0。
| 按位或(|),参与运算的两数各对应的二进位相或。是0 结果为 0,否则为 1。
^ 按位异或(^),参与运算的两数各对应的二进位相异或。不同则为 1,相同为 0。
&^ 位清空(&^),将指定位置上的值设置为 0。a &^ b,对于b上的每个数值,如果为 0,则取 a 对应位上的数值,如果为1,则取 0。
<< 左移(<<),左移n位就是乘以2的n次方。其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
>> 右移(>>),右移n位就是除以2的n次方。其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。
package main
import "fmt"
func main() {
var a int = 30
var b int = 15
// a的二进制为:11110,b的二进制为:1111
fmt.Printf("a=%d, 其二进制值为: %b\n", a, a)
fmt.Printf("b=%d, 其二进制值为: %b\n", b, b)
// &: 都是1 结果为 1,否则为 0。
/*
0001 1110
&
0000 1111
——————————
0000 1110
*/
// 30 & 15 = 14, 11110 & 1111 = 1110
fmt.Printf("%d & %d = %d, %b & %b = %b\n", a, b, a&b, a, b, a&b)
// |: 都是0 结果为 0,否则为 1。
/*
0001 1110
|
0000 1111
——————————
0001 1111
*/
// 30 | 15 = 31, 11110 | 1111 = 11111
fmt.Printf("%d | %d = %d, %b | %b = %b\n", a, b, a|b, a, b, a|b)
// ^: 不同则为 1,相同为 0。
/*
0001 1110
^
0000 1111
——————————
0001 0001
*/
// 30 ^ 15 = 17, 11110 ^ 1111 = 10001
fmt.Printf("%d ^ %d = %d, %b ^ %b = %b\n", a, b, a^b, a, b, a^b)
// &^: 对于b上的每个数值,如果为 0,则取 a 对应位上的数值,如果为1,则取 0。
/*
0001 1110
&^
0000 1111
——————————
0001 0000
*/
// 30 &^ 15 = 16, 11110 &^ 1111 = 10000
fmt.Printf("%d &^ %d = %d, %b &^ %b = %b\n", a, b, a&^b, a, b, a&^b)
// <<: 把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
/*
0001 1110 << 2
——————————————
0111 1000
*/
// 30 << 2 = 120, 11110 << 2 = 1111000
fmt.Printf("%d << 2 = %d, %b << 2 = %b\n", a, a<<2, a, a<<2)
// >>: 把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。
/*
0001 1110 >> 2
——————————————
0000 0111
*/
// 30 >> 2 = 7, 11110 >> 2 = 111
fmt.Printf("%d >> 2 = %d, %b >> 2 = %b\n", a, a>>2, a, a>>2)
}
赋值运算符
运算符 描述
= 简单的赋值运算符,将一个表达式的值赋给一个左值,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 ^= 2 等于 C ^= C ^ 2
package main
import "fmt"
func main() {
var a int = 30
var b int = 15
var c int
// c = 30 + 15, c的值为: 45
fmt.Printf("c = %d + %d, c的值为: %d\n", a, b, a+b)
// c += 30, c的值为: 40
c = 10
c += a
fmt.Printf("c += %d, c的值为: %d\n", a, c)
// c -= 30, c的值为: -20
c = 10
c -= a
fmt.Printf("c -= %d, c的值为: %d\n", a, c)
// c *= 30, c的值为: 300
c = 10
c *= a
fmt.Printf("c *= %d, c的值为: %d\n", a, c)
// c /= 30, c的值为: 0
c = 10
c /= a
fmt.Printf("c /= %d, c的值为: %d\n", a, c)
// c %= 30, c的值为: 10
c = 10
c %= a
fmt.Printf("c %%= %d, c的值为: %d\n", a, c)
// c的二进制值为: 1010, c <<= 2, c的值为: 40, c的二进制值为: 101000
c = 10
fmt.Printf("c的二进制值为: %b, ", c)
c <<= 2
/*
0000 1010 << 2
———————————————
0010 1000
*/
fmt.Printf("c <<= 2, c的值为: %d, c的二进制值为: %b\n", c, c)
// c的二进制值为: 1010, c >>= 2, c的值为: 2, c的二进制值为: 10
c = 10
fmt.Printf("c的二进制值为: %b, ", c)
c >>= 2
/*
0000 1010 >> 2
———————————————
0000 0010
*/
fmt.Printf("c >>= 2, c的值为: %d, c的二进制值为: %b\n", c, c)
// c的二进制值为: 1010, 2的二进制值为: 10, c &= 2, c的值为: 2, c的二进制值为: 10
c = 10
fmt.Printf("c的二进制值为: %b, 2的二进制值为: %b, ", c, 2)
c &= 2
/*
0000 1010
&
0000 0010
———————————————
0000 0010
*/
fmt.Printf("c &= 2, c的值为: %d, c的二进制值为: %b\n", c, c)
// c的二进制值为: 1010, 2的二进制值为: 10, c |= 2, c的值为: 10, c的二进制值为: 1010
c = 10
fmt.Printf("c的二进制值为: %b, 2的二进制值为: %b, ", c, 2)
c |= 2
/*
0000 1010
|
0000 0010
———————————————
0000 1010
*/
fmt.Printf("c |= 2, c的值为: %d, c的二进制值为: %b\n", c, c)
// c的二进制值为: 1010, 2的二进制值为: 10, c ^= 2, c的值为: 8, c的二进制值为: 1000
c = 10
fmt.Printf("c的二进制值为: %b, 2的二进制值为: %b, ", c, 2)
c ^= 2
/*
0000 1010
^
0000 0010
———————————————
0000 1000
*/
fmt.Printf("c ^= 2, c的值为: %d, c的二进制值为: %b\n", c, c)
}
其他运算符
运算符 描述
& 返回变量存储地址;&a;将给出变量的实际地址
* 指针变量;*a;是一个指针变量
package main
import "fmt"
func main() {
var a string = "hello"
// &a的类型: *string, a的内存地址: 0xc00004e250
fmt.Printf("&a的类型: %T, a的内存地址: %v\n", &a, &a)
var ptr *string
ptr = &a
// *ptr的类型: string, *ptr为: hello
fmt.Printf("*ptr的类型: %T, *ptr为: %v\n", *ptr, *ptr)
}
类型别名
package main
import "fmt"
type newInt int
func main() {
var a, b newInt = 1, 2
c := a + b
// c 的类型: main.newInt, c 的值: 3
fmt.Printf("c 的类型: %T, c 的值: %d", c, c)
//var d int = 3
// 编译错误 Invalid operation: a+d (mismatched types newInt and int)
//e := a+d
}
- 在代码中,我们可以给某个类型重新定义一个名字。(用于简化名称或解决名称冲突)
- 类型别名定义格式:
type 新名字 类型
- 类型别名得到的新类型并非与原类型完全相同,新类型不会拥有原类型所附带的方法。
Go语言中的控制结构
在Go语言中程序有几种执行代码的结构:
- 顺序结构:自上而下执行
- 选择结构:满足一定条件才会执行
- 循环结构:条件满足的情况下,会去循环执行 0~N 次
在前面,所了解到的Go程序,都是从 main() 函数开始执行,然后按顺序执行该函数体中的代码。但是在实际的编程中,我们经常会需要对一些特殊情况进行判断,并在满足一些特定条件的情况下才执行某些代码,也就是在代码中进行条件判断。
在Go语言中提供了以下几种条件判断语句:
if
语句if...else...
语句if
嵌套语句switch
语句select
语句
注意:Go语言中没有三目运算符,所以不支持 ?: 形式的条件判断
if
if 条件:bool{}
,可以单独存在if 条件:bool{}...else...
if 条件:bool{}...else if 条件:bool{}...else if 条件:bool{}...else
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
if a < b {
fmt.Println("a的值比b的值大")
}
if a > b {
fmt.Println("a的值比b的值大")
} else {
fmt.Println("a的值比b的值小")
}
}
package main
import "fmt"
func main() {
for {
var username string
var pwd string
fmt.Println("请输入账号:")
fmt.Scan(&username)
fmt.Println("请输入密码:")
fmt.Scan(&pwd)
if username == "root" && pwd == "123" {
fmt.Println("[管理员] 登录成功!")
} else if username == "user1" && pwd == "123" {
fmt.Println("[user1] 登录成功!")
} else {
fmt.Println("登录失败!")
}
}
}
switch
switch的使用
switch - case
switch - case - fallthrough
switch - case - default
package main
import "fmt"
func main() {
var name string
for {
fmt.Println("请输入您的名字:")
fmt.Scan(&name)
switch name {
case "user1":
fmt.Println("user1")
case "user2":
fmt.Println("user2")
default:
fmt.Println("名字不存在")
}
}
}
package main
import "fmt"
func main() {
var name string
for {
fmt.Println("请输入您的名字:")
fmt.Scan(&name)
// switch可以省略条件,默认是 switch true
switch {
case name == "user1":
fmt.Println("user1")
case name == "user2":
fmt.Println("user2")
default:
fmt.Println("名字不存在")
}
}
}
case穿透:fallthrough
package main
import "fmt"
func main() {
boolean := 1
/*
输出:
case1
case2
*/
switch boolean {
case 1:
fmt.Println("case1")
// 在 case 中,一旦使用了 fallthrough,则会强制执行下一个 case 语句
fallthrough
case 2:
fmt.Println("case2")
// break 可以跳出并终止这个 case,也可以退出 case 穿透
break
fallthrough
case 3:
fmt.Println("case3")
default:
fmt.Println("default")
}
}
select
- select 只能用于通道的读写操作
- select 中的case条件(非阻塞)是并发执行的,select会选择先操作成功的那个case条件去执行,如果多个同时返回,则随机选择一个执行,此时将无法保证执行顺序。对于阻塞的 case 语句会直到其中有信道可以操作;如果有多个信道可操作,会随机选择其中一个 case 执行。
for
在Go语言中,如果想要重复执行某些语句,则只有使用 for 结构。
基于计数器的迭代
基本形式:
for 初始化语句; 条件语句; 修饰语句(自增或自减) {}
package main
import "fmt"
func main() {
// 循环打印 5次 hello
for i := 0; i < 5; i++ {
fmt.Println("hello, 打印次数:", i)
}
}
基于条件判断的迭代
基本形式:
for 条件语句 {}
package main
import "fmt"
func main() {
var i int = 5
for i >= 0 {
fmt.Println("i=", i)
i -= 1
}
}
无限循环
基本形式:
for {}
for循环中,如果头部没有条件语句,则会认为条件永远为 true,因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。
package main
import "fmt"
func main() {
var a int
for {
fmt.Println("请输入一个数字:")
fmt.Scan(&a)
fmt.Println("输入了一个:", a)
}
}
for-rang 结构
基本形式:
for 下标, 值 := range coll {}
package main
import "fmt"
func main() {
var nums = [4]int{1, 2, 3, 4}
/*
0 1
1 2
2 3
3 4
*/
for i, i1 := range nums {
fmt.Println(i, i1)
}
}
for 终止循环
- break:结束整个循环,立即停止。
- continue:结束当前这次循环,继续执行下一次循环。
- goto:将控制转移到被标记的语句。
package main
import "fmt"
func main() {
/*
0
1
*/
for i := 0; i < 5; i++ {
if i == 2 {
break
}
fmt.Println(i)
}
}
package main
import "fmt"
func main() {
/*
0
1
3
4
*/
for i := 0; i < 5; i++ {
if i == 2 {
continue
}
fmt.Println(i)
}
}
package main
import "fmt"
func main() {
var a int = 10
/*
a的值为:10
a的值为:11
a的值为:12
a的值为:13
a的值为:14
a的值为:16
a的值为:17
a的值为:18
a的值为:19
*/
LOOP:
for a < 20 {
if a == 15 {
a = a + 1
// 在变量a等于15的时候跳过本次循环并回到循环的开始语句 LOOP 处
goto LOOP
}
fmt.Printf("a的值为:%d\n", a)
a++
}
}
嵌套for循环
使用for循环打印 九九乘法表
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d \t", j, i, i*j)
}
fmt.Println()
}
}