Day001 Go 基础环境搭建

157 阅读10分钟

01. 关于 Go

编译型语言(性能高),Go 语言 => 编辑器 => 可执行文件 => 操作系统 + CPU 执行。

解释型语言(跨平台好)。

02. 下载 Go

下载 Go,直接下一步安装。

D:\Soft\Go\bin\go.exe 编译器。

# 查看和 Go 相关的环境变量的情况
go env
# 比较关键的有下面几个
# GO111MODULE=on
# GOROOT=D:\Soft\Go
# GOPATH=C:\Users\dangp\go
# GOPROXY=https://goproxy.cn,direct

如何关闭 GOMOD?

go env -w "GO111MODULE=off"
# 配置环境变量,默认安装后是有的
GOPATH C:\Users\dangp\go

# 下面的三个文件
# src,存放源代码(手动创建)
# pkg,存储编译后生成的包文件,例如第三方包
# bin,可执行文件

# 查看安装的 Go 版本
go version
# go version go1.23.4 windows/amd64

03. 第一个 Go 程序

package main

// 会去 GOROOT/src 下面查找 fmt
import "fmt"

func main() {
  fmt.Println("Hello World")
}
go build # 会自动找到 main 包,生成一个可执行的二进制文件
go build -o 包名

# 可以将编译和执行二合一
go run test.go

04. 下载 GoLand

www.jetbrains.com.cn/go/download…

main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

创建项目时,保证 GOROOT 的位置正确。

如何运行 Go 文件?

# go build # 会找 main 包进行构建
# go build -o 可执行文件.exe
go run main.go

05. 变量和常量

5.1 如何定义变量

变量是对具体值的引用。

  1. 先声明再赋值。
var name string
name = "危险"
fmt.Println(x)
  1. 声明和赋值一起。
var name string = "危险"

// 声明和赋值一起的时候,也可以省略类型
var name = "危险"
  1. 变量的默认值是类型的默认值。
var age int
// 默认是类型的默认值
fmt.Println(age) // 0
  1. 可以一次性声明多个变量。
var (
    name    string
    address string
)
name = "危险"
address = "河南"
fmt.Println(name, address)
var a, b, c = 1, 2, 3
fmt.Println(a, b, c)

5. 短变量(推荐)。

// 可以省略 var 和数据类型
name := "危险"
age := 18
fmt.Println(name, age)

5.2 如何交换变量

// 注意点:a 和 b 的类型要保持一致
a := 4
b := 7
a, b = b, a
fmt.Println(a, b) // 7 4

5.3. 如何打印内存地址?

运算符描述实例
&返回变量存储地址例如 &a,将给出变量的实际地址
*指针变量。例如 *a,表示一个指针变量
package main

import "fmt"

func main() {
    name := "危险"

    // %d 是数字 => decimal
    // %s 是字符串
    // 注意这儿用的是 Printf,不是 Println
    fmt.Printf("name 的值是:%s,内存地址是:%p", name, &name)
}

5.4 什么是匿名变量

var _, b = 1, 2
fmt.Println(b) // 2
package main

import "fmt"

func test() (int, int) {
    return 10, 20
}

func main() {
    // 匿名变量
    a, _ := test()
    fmt.Println(a) // 10
}

5.5 如何定义常量

package main

func main() {
    // 定义常量推荐大写
    // 1. 常量定义了可以不使用
    // 2. 常量不能使用 := 简写
    // 3. 常量和变量放置的内存地址不同,常量是放在常量池中的,正常是取不到地址的
    // 4. 常量也可以如下方式定义多个
    const URL, API string = "wps.cn", "https://www.google.com"
}

5.6 iota(了解)

package main

import "fmt"

// iota 常量计数器(一组中的),默认是 0,每定义一个常量 iota 的值就会 +1
func main() {
    const (
       a = iota
       b = iota
       c = iota
       d = 0
       e = iota
       // 如果在定义 const 的时候,如果下面的常量没有赋值,默认沿用上面一个常量定义的赋值,例如下面的 f,g 就是 iota
       f
       g
       h
       // ... 1000+
    )
    const (
       i = iota
       j = 8    // iota + 1
       k = iota // iota + 1 = 2
    )
    fmt.Println(a, b, c, d, e, f, g, h) // 0 1 2 0 4 5 6 7
    fmt.Println(i, j, k)                // 0 8 2
}

5.7 关于全局和局部变量

package main

import "fmt"

// 全局变量
// 1. 定义在 Go 文件的非函数内
// 2. 全局变量必须使用 var 关键字
// 3. 全局变量定义了可以不使用
var c int

func main() {
    // 局部变量
    a := 1
    b := 2
    c = a + b
    // 如果全局变量有,用的是全局的
    fmt.Printf("c的地址是: %p", &c) // 0xbbc570

    // 如果局部变量有,用的是局部的
    c := a + b
    fmt.Printf("c的地址是: %p", &c) // 0xc00000a0f8
    c = 8
    // 就近原则
    fmt.Printf("c的地址是: %p", &c) // 0xc00000a0f8
    fmt.Println(c)
}

06. 基本数据类型

位(bit):计算机中数据储存的最小单位,字节(Byte):计算机中数据存储的单位。

1 Byte = 8 bit

6.1 整型

image.png

类型取值范围
int8-128到127
uint80 到 255
int16-32768 到 32767
uint160 到 65535
int32-2147483648 到 2147483647
uint320 到 4294967295
int64-9223372036854775808 到 9223372036854775807
uint640 到 18446744073709551615
uint与平台相关,32 位操作系统上就是 uint32,64 位操作系统上就是 uint64
int与平台相关,32 位操作系统上就是 int32,64 位操作系统上就是 int64

如何计算?

uint32 => 0 ~ Math.pow(2, 32) - 1
int32 => -Math.pow(2, 31) ~ Math.pow(2, 31) - 1

越界报错演示?

var x int
x = 9223372036854775808
fmt.Print(x) // overflows int

// cannot use 9223372036854775808 (untyped int constant) as int value in assignment (overflows)

如何进行进制转换?

var a int = 10
fmt.Printf("%d \n", a) // 10    占位符%d表示十进制
fmt.Printf("%b \n", a) // 1010  占位符%b表示二进制
fmt.Printf("%o \n", a) // 12    占位符%o表示八进制
fmt.Printf("%x \n", a) // a     占位符%x表示十六进制

// 0 开始的表示八进制,例如 020,0x 开始的表示十六进制,例如 ox12

如何查看类型?

package main

import (
    "fmt"
    "reflect"
)

func main() {
  var x = 1
  fmt.Println(reflect.TypeOf(x)) // int
}

6.2 特殊的类型 bite 和 rune

package main

import "fmt"

func main() {
    // uint8 的别名是 byte,十分常用
    // 常用来表示 0 ~ 255 之间的整数
    var num1 byte = 255
    fmt.Printf("%T\n", num1)

    // int32 的别名是 rune
    var num2 rune = 1000000000
    fmt.Printf("%T\n", num2)
}

6.3 浮点型

浮点型有两种,float32 和 float64,默认为 float64(可保留的小数点位数更多)

var f3 = 1.47
fmt.Println(f3, reflect.TypeOf(f3)) // 默认 float64
//3 乘以 10 的 -2 次方
var x = 3e-2
var y = 4e2
fmt.Println(x, y) // 0.03, 400

6.4 布尔

var b1 = true
fmt.Println(reflect.TypeOf(b1)) // bool

6.5 字符串

6.5.1 基本操作

var str = "hello world"

// 1. 索引取值
s1 := str[1]
fmt.Println(s1)         // 101
fmt.Println(string(s1)) // e

// 2. 通过切片的形式取值
s2 := str[2:5]
fmt.Println(s2)                 // llo
fmt.Println(reflect.TypeOf(s2)) // string

// 3. 字符串拼接
s3 := str + " 123"
fmt.Println(s3) // hello world 123

6.5.2 转义符

var str = "hello world "🤠""
fmt.Println(str) // hello world "🤠"

6.5.3 多行字符串

通过 `` 包裹的能严格保留内部的格式。

var str = `
       1. a
       2. b
    `
fmt.Println(str)

6.5.4 常用方法

方法介绍
len(str)求长度
strings.ToUpper,strings.ToLower生成一个新的全部大写的字符串,生成一个新的全部小写的字符串
strings.ReplaceAll生成一个新的原字符串被指定替换后的字符串
strings.Contains判断是否包含
strings.HasPrefix,strings.HasSuffix前缀/后缀判断
strings.Trim去除字符串两端匹配的内容
strings.Index(),strings.LastIndex()子串出现的位置
strings.Split分割,将字符串按指定的内容分割成数组
strings.Join(a[]string, sep string)join操作,将数组按指定的内容拼接成字符串

07. 类型转换

Go 语言不存在隐式类型转换,所有的类型转换都必须显式的声明:A = A(B)

新类型的值 = 新类型(旧类型的值)
package main

import "fmt"

func main() {
    // 浮点数转整数,会截断,只保留整数部分
    a := 5.9    // float
    b := int(a) // b 就是 int 类型的 5

    fmt.Printf("%T,%.1f\n", a, a) // float64, 5.9
    fmt.Printf("%T,%d\n", b, b)   // int, 5
    
    // 整数转浮点数
    c := 1
    d := float64(c)
    fmt.Printf("%T,%d\n", c, c) // int, 1
    fmt.Printf("%T,%f\n", d, d) // float64, 1.000000

    // 布尔类型转换,布尔类型是不支持转换为 int
    // var flag bool = true
    // f := int(flag)
}
// 1. 整形之间转换
var a int8 = 20
// int8 转 int64
fmt.Println(int64(a), reflect.TypeOf(int64(a))) // 20, int64
// int8 转 float64
fmt.Println(float64(a), reflect.TypeOf(float64(a))) // 20, float64

// 2. string 与 int 类型的转换
// int 转 string
x := strconv.Itoa(98)
fmt.Println(x, reflect.TypeOf(x)) // 98, string

// string 转 int
y, _ := strconv.Atoi("98")
fmt.Println(y, reflect.TypeOf(y)) // 98, int

// 3. Parse
// 把数字型的字符串转成 int
// 参数1:数字型的字符串
// 参数2:数字型字符串的进制
// 参数3:如果字符串转换的结果超过了 bitSize 的最大值,那么则输出 intBitSize 的最大值
x1, _ := strconv.ParseInt("1000", 10, 8)
fmt.Println(x1) // 127
x2, _ := strconv.ParseInt("1000", 10, 64)
fmt.Println(x2) // 1000

// 把浮点型的字符串转换成 Float
x3, _ := strconv.ParseFloat("1.211", 64)
fmt.Println(x3, reflect.TypeOf(x3)) // 1.211, float64

// 把布尔型的字符串转换成 Boolean
b1, _ := strconv.ParseBool("true")
fmt.Println(b1, reflect.TypeOf(b1)) // true, bool

08. 关于分支

fmt.Scan($num1),接受输入,注意参数要是一个地址。

package main

import "fmt"

// 需求:两次输入 123456,提示登录成功
func main() {
    var num1 int
    var num2 int
    // 提示用户输入
    fmt.Printf("请输入密码 : \n")
    //  &num1 地址
    fmt.Scan(&num1)
    if num1 == 123456 {
       fmt.Println("请再次输入密码: ")
       fmt.Scan(&num2)
       if num2 == 123456 {
          fmt.Println("登录成功!")
       } else {
          fmt.Println("登录失败!")
       }
    } else {
       fmt.Println("登录失败")
    }
}

switch 比 if else 更简洁,效率更高!

var choice int
fmt.Println("Choose a number:")
fmt.Scanln(&choice)
switch choice {
case 1:
    fmt.Println("牛")
case 2:
    fmt.Println("牛牛")
case 3:
    fmt.Println("牛牛牛")
case 4:
    fmt.Println("牛牛牛牛")
default:
    fmt.Println("输入有误")
}
package main

import "fmt"

func main() {

    a := false

    // 这里的爆红不影响
    switch a {
    case false:
       fmt.Println("1")
       fallthrough // 在 case 中一旦使用了 fallthrough,则会强制执行下一个 case 语句
    case true:
       fmt.Println("2")
    case false:
       fmt.Println("3")
    default:
       fmt.Println("6")
    }
}

09. 关于循环

求 1 ~ 100 的和。

var s = 0
for i := 1; i <= 100; i++ {
    s += i
}
fmt.Println(s) // 5050

无限循环。

for true {
    var choice int
    fmt.Println("Choose a number:")
    fmt.Scanln(&choice)
    switch choice {
    case 1:
       fmt.Println("牛")
    case 2:
       fmt.Println("牛牛")
    case 3:
       fmt.Println("牛牛牛")
    case 4:
       fmt.Println("牛牛牛牛")
    default:
       fmt.Println("输入有误")
    }
}

打印一个十行十列的矩形。

for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
       fmt.Print(" * ")
    }
    fmt.Print("\n")
}

直接三角形。

 * 
 *  * 
 *  *  * 
 *  *  *  * 
 *  *  *  *  * 
 *  *  *  *  *  * 
 *  *  *  *  *  *  * 
 *  *  *  *  *  *  *  * 
 *  *  *  *  *  *  *  *  * 
for i := 0; i < 10; i++ {
    for j := 0; j < i; j++ {
       fmt.Print(" * ")
    }
    //fmt.Print("\n")
    fmt.Println()
}

10. 输出输出

10.1 输出函数

  1. fmt.Print(),不换行输出
  2. fmt.Println(),换行输出
  3. fmt.printf(),格式化输出
name := "John Doe"
age := 18
isMarried := false
salary := 123.4

fmt.Printf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Printf("姓名:%v 年龄:%v 婚否:%v 薪资:%v\n", name, age, isMarried, salary)
  1. fmt.Sprintf(),不会输出到终端,而是返回加工后的字符串
// ...
info := fmt.Sprintf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Println(info) // 姓名:John Doe 年龄:18 婚否:false 薪资:123.40

10.2 输入函数

  1. fmt.Scan()

输入求和。

var a, b int
fmt.Print("请输入第一个数字:")
fmt.Scan(&a)
fmt.Print("请输入第一个数字:")
fmt.Scan(&b)
fmt.Println("合计:", a+b)
  1. fmt.Scanf(),输入多个时,可以以指定的形式隔开
var a, b int
fmt.Scan(&a, &b) // 输入:1 2
//fmt.Scanf("%d + %d", &a, &b) // 输出:1 + 2
fmt.Println(a + b)
  1. fmt.Scanln()

了解 Scan 和 Scanln 的区别,如果是 Scan,先输入 1,再敲回车,会继续等待,可以再输入 2,而 Scanln 是遇到回车就结束啦。

package main

import "fmt"

func main() {
    var a, b int
    fmt.Print("请输入两个整数,用空格分隔,然后按回车键: ")
    fmt.Scanln(&a, &b)
    fmt.Printf("你输入的两个整数是: %d 和 %d\n", a, b)
}