1-golang基础(基本数据类型、指针、运算符)
1. sdk下载:
2. windows下安装sdk
- 下载windows sdk,并安装
- 配置环境变量,go1.17.6.windows-amd64.msi安装后会默认配置GOROOT,只需要重新配置GOPATH,用于指向工作目录(项目存放目录)
- 验证是否安装成功
go version
3. golang执行流分析
两种执行流:
- XX.go文件通过执行go build XX.go生成XX.exe文件,执行XX.exe得到结果
- go run XX.go直接执行得到结果
两种执行流的区别
- 通过go build得到的.exe文件,在没有go环境的机器上也可以运行
- go run XX.go需要有go开发环境才能执行
- go build得到的.exe文件相比原来的.go文件,内存变大了很多,是因为编译器会将程序运行依赖的库文件包含在可执行文件中
通过go build可以生成指定文件名的可执行文件
go build -o myhello.exe hello.go
4. golang 开发注意事项
- golang源文件以.go为扩展名
- go应用程序执行入口是main()函数
- 严格区分大小写
- 每个语句后面不需要加分号
- 不能把多条一句写在一行,否则会报错
- 定义的变量或者import的包,如果没有使用,需要删掉,否则会报错
- 方法体的左大括号必须和方法名同一行,否则会报错
5. golang变量使用
package main
import "fmt"
// 定义全局变量
var b = 3
var c = "cc"
var (
d = 4
e = "ee"
)
func main() {
// 第一种:完整定义并赋值
var a int = 1
fmt.Println("a=", a)
// 第二种:指定变量类型,若不赋值,则使用默认值
var i int
fmt.Println("i=", i)
// 第三种:根据值自行判断变量类型(类型推导)
var num = 10.11
fmt.Println("num=", num)
// 第四种:省略var,注意 := 左侧的变量不能是之前申明过的,否则会报错
// 下面方式等价于 var name string = "tom"
name := "tom"
fmt.Println("name=", name)
// 一次申明多个变量
var n1, n2, n3 int
fmt.Println("n1=", n1, "n2=", n2, "n3=", n3)
var n4, n5, n6 = 4, "tom", 6
fmt.Println("n4=", n4, "n5=", n5, "n6=", n6)
n7, n8, n9 := 7, "tom8", 9
fmt.Println("n7=", n7, "n8=", n8, "n9=", n9)
fmt.Println("b=", b, "c=", c, "d=", d, "e=", e)
// 加号的使用
// 两边是数值类型,则是做加法运算
var m1 = 1
var m2 = 2
var m3 = m1 + m2
fmt.Println("m3=", m3)
// 两边有字符串,则是做拼接
var m4 = "m4"
var m5 = "m5"
var m6 = m4 + m5
fmt.Println("m6=", m6)
}
6. golang数据类型
1. 整数类型
类型 | 有无符号 | 占用存储空间 | 数据范围 | 备注 |
int8 | 有 | 1字节 | -128 ~ 127 | |
int16 | 有 | 2字节 | -2^15 ~ 2^15-1 | |
int32 | 有 | 4字节 | -2^31 ~ 2^31-1 | |
int64 | 有 | 8字节 | -2^63 ~ 2^63-1 | |
uint8 | 无 | 1字节 | 0 ~ 255 | |
uint16 | 无 | 2字节 | 0 ~ 2^16-1 | |
uint32 | 无 | 4字节 | 0 ~ 2^32-1 | |
uint64 | 无 | 8字节 | 0 ~ 2^64-1 | |
int | 有 | 32位系统4字节,64位系统8字节 | 2^31 ~ 231-1,-263 ~ 2^63-1 | |
uint | 无 | 32位系统4字节,64位系统8字节 | 0 ~ 2^32-1,0 ~ 2^64-1 | |
rune | 有 | 与int32一样 | 2^31 ~ 2^31-1 | 与int32一样,表示一个unicode码,存储中文字符用rune |
byte | 无 | 与uint8一样 | 0 ~ 255 | 与uint8一样,要存储字符时选择byte |
golang中整型默认声明为int型
// 查看数据类型
var a1 = 100
fmt.Printf("a1的类型是 %T", a1)
// 查看数据类型和占用字节数
var a2 int8 = 100
fmt.Printf("a2的类型是 %T, 占用的字节数是 %d", a2, unsafe.Sizeof(a2))
2. 浮点类型
类型 | 占用存储空间 | 数据范围 |
单精度float32 | 4字节 | -3.403E38 ~ 3.403E38 |
双精度float64 | 8字节 | -1.798E308 ~ 1.798E308 |
golang中浮点型默认声明为float64,开发中推荐使用float64,精度更高
3. 字符类型
var b1 byte = 'a'
// 如果直接输出byte,得到的是字符对应的ASCII码
fmt.Println("b1=", b1)
// 如果希望输出字符,需要使用格式化输出
fmt.Printf("b1=%c", b1)
4. 布尔类型
bool只能是true或者false,占1个字节
5. 字符串类型
golang中采用的utf8编码,不用担心中文乱码问题;
字符串一般用双引号(“”),想输出代码块可以用反引号(``)
str1 := "abc\nabc"
fmt.Println(str1)
str2 := `
var b1 byte = 'a'
// 如果直接输出byte,得到的是字符对应的ASCII码
fmt.Println("b1=", b1)
// 如果希望输出字符,需要使用格式化输出
fmt.Printf("b1=%c", b1)
`
fmt.Println(str2)
当一个字符串很长,需要+拼接是,+必须放在上一行,否则会报错
str3 := "hello " + "world " +
"hello " + "world " +
"hello " + "world " +
"hello " + "world " +
"hello " + "world "
fmt.Println(str3)
6. 基本数据类型的默认值
数据类型 | 默认值 |
整数类型 | 0 |
浮点类型 | 0 |
布尔类型 | false |
字符串类型 | “” |
7. 基本数据类型的相互转换
golang在数据类型的转换需要显示转换,不能自动转换
var i int32 = 100
var m int64 = int64(i)
var n int8 = int8(i)
fmt.Printf("i=%d, m=%d, n=%d", i, m, n)
注意:被转换的是值,变量本身的数据类型并没有转换。上面的例子,转换完以后,i依然是int32
// 大范围转小范围,可能会溢出,但不会报错,结果和我们期望的不一样
var n1 int64 = 99999999
var n2 int8 = int8(n1)
fmt.Println(n2)
8. 基本数据类型转string
a := 100
b := 101.1
c := true
d := 'h'
// 第一种方式 fmt.Sprintf 转换
var str1 string = fmt.Sprintf("%d", a)
var str2 string = fmt.Sprintf("%f", b)
var str3 string = fmt.Sprintf("%t", c)
var str4 string = fmt.Sprintf("%c", d)
fmt.Printf("str1=%q str2=%q str3=%q str4=%q", str1, str2, str3, str4)
// 第二种方式 strconv函数转换
// 10 代表10进制
var str5 string = strconv.FormatInt(int64(a), 10)
// 'f' 代表格式; 10 代表保留10个小数位; 64 表示这个小数是float64
var str6 string = strconv.FormatFloat(b, 'f', 10, 64)
var str7 string = strconv.FormatBool(c)
fmt.Printf("str5=%q str6=%q str7=%q", str5, str6, str7)
// strconv.FormatInt 简写
var str8 string = strconv.Itoa(a)
fmt.Printf("str8=%q", str8)
9. string转基本数据类型
str1 := "true"
// strconv.ParseBool(str)有两个返回值 (value bool, err error)
// 我们只需要获取value bool的值,所以用 _ 忽略error
a, _ := strconv.ParseBool(str1)
fmt.Printf("a=%t", a)
str2 := "1000"
// strconv.ParseInt(str)的返回值是int64和error
b, _ := strconv.ParseInt(str2, 10, 0)
fmt.Printf("b=%v", b)
str3 := "123.456"
// strconv.ParseFloat(str)的返回值是float64和error
c, _ := strconv.ParseFloat(str3, 64)
fmt.Printf("c=%v", c)
// 注意:如果转换失败了,会存储一个默认值
str4 := "hello"
var d int64 = 12
d, _ = strconv.ParseInt(str4, 10, 64)
// 输出结果为默认值0
fmt.Printf("d=%v", d)
10. 指针
- 基本数据类型变量存的就是值,也叫值类型
- 值类型包括基本数据类型int系列、float系列、bool、string、数组和结构体(struct)
- 获取值类型变量的地址,用&,例如:&i
- 指针变量本身占用一个地址,里面存的是也是一个地址(例如:var ptr *int = &i),这个地址指向的是值类型变量的值
- 获取指针变量所指向的值,用*,例如 *ptr
func main() {
i := 10
// 获取i的地址
fmt.Println("i的地址=", &i)
// ptr是一个指针变量,类型是 *int (int型的指针)
// ptr存储的值是 &i,也就是i的地址
var ptr *int = &i
fmt.Printf("ptr=%v\n", ptr)
// ptr本身也有一个地址
fmt.Printf("ptr的地址=%v\n", &ptr)
// 获取ptr指向的值
fmt.Printf("ptr指向的值=%v\n", *ptr)
}
11. 值类型和引用类型
值类型:int系列、float系列、bool、string、数组、结构体(struct);
引用类型:指针、切片(slice)、map、管道(channel)、interface;
值类型变量直接存储值,内存在栈中分配;
引用类型变量存储的是地址,这个地址对应的空间存储的是值,内存在堆中分配,没有任何变量引用这个地址时,等待GC回收
7. 标识符命名规范
- 由26个英文字母大小写,0~9, _ 组成
- 数字不能开头
- 严格区分大小写 (例如:var num int 和 var Num int是不一样的)
- 不能包含空格
- _ 是空标识符,可以代表任何其他标识符,此时会忽略其他标识符对应的值,因此仅被作为占位符使用,自定义标识符中不可单独使用 _
- 保留关键字不能作为标识符(共25个),例如:break、case、chan、const、continue、default、defer、else、fallthrough、for、func、go、goto、if、import、interface、map、package、range、return、select、struct、switch、type、var
注意事项:
- 包名和目录尽量保持一致,不要和标准库冲突
- 变量名、函数名、常量名采用驼峰法
- 如果变量名、函数名、常量名首字母大写,则可以被其他包访问;如果首字母小写,则只能在本包中使用;可以简单理解为首字母大写的是共有的,小写的是私有的
8. 运算符
注意:golang中没有三元运算符
1. 算术运算符
+ - * / % ++ --
// 如果运算的都是整数,除法运算以后,结果也是整数,小数部分被截取掉
// 结果为2
fmt.Println(10 / 4)
// 结果还是2
var n1 float32 = 10 / 4
fmt.Println(n1)
// 如果希望保留小数部分,则需要有浮点数参与运算
var n2 float32 = 10.0 / 4
fmt.Println(n2)
// 取模计算公式 a % b = a - a / b * b
fmt.Println("10 % 3=", 10%3)
fmt.Println("-10 % 3=", -10%3)
fmt.Println("10 % -3=", 10%-3)
fmt.Println("-10 % -3=", -10%-3)
// ++ 和 --只能独立作为一行语句使用,不能放在表达式中使用
// 只有 后++ 和 后--; 没有 前++ 和 前--
2. 关系运算符
== != > < >= <=
3. 逻辑运算符
&& || !
4. 赋值运算符
= += -= *= /= %=
func main() {
// 两个变量 a 和 b,在不引入中间变量的情况下,交换 a 和 b的值
a := 10
b := 20
fmt.Printf("交换前 a=%d b=%d \n", a, b)
a = a + b
b = a - b // b = a + b - b => b = a
a = a - b // a = a + b - a
fmt.Printf("交换后 a=%d b=%d \n", a, b)
}
5. 位运算符
// 按位与& : 两位全为1,结果为1,否则为0
// 按位或| : 两位有一个为1,结果为1,否则为0
// 按位异或^ : 两位一个为0,一个为1,结果为1,否则为0
// 右移>> : 低位溢出,符号位不变,并用符号位补溢出的高位
// 左移<< :符号位不变,低位补0
1. 原码、反码、补码
对于有符号的而言:
- 二进制最高位是符号位:0表示整数,1表示负数
- 正数的原码、反码、补码都一样
- 负数的反码是原码的符号位不变,其他位取反
- 负数的补码是它的反码+1
1 => 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
-1 => 原码[1000 0001] 反码[1111 1110] 补码[1111 1111] - 0的反码补码都是0
- 计算机运算的时候,都是以补码的方式来运算的
运算符
// 按位与& : 两位全为1,结果为1,否则为0
// 按位或| : 两位有一个为1,结果为1,否则为0
// 按位异或^ : 两位一个为0,一个为1,结果为1,否则为0
// 右移>> : 低位溢出,符号位不变,并用符号位补溢出的高位
// 左移<< :符号位不变,低位补0
1. 原码、反码、补码
对于有符号的而言:
- 二进制最高位是符号位:0表示整数,1表示负数
- 正数的原码、反码、补码都一样
- 负数的反码是原码的符号位不变,其他位取反
- 负数的补码是它的反码+1
1 => 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
-1 => 原码[1000 0001] 反码[1111 1110] 补码[1111 1111] - 0的反码补码都是0
- 计算机运算的时候,都是以补码的方式来运算的