# day01 基本数据类型、运算符、流程控制

# 搭建开发环境

# 下载地址

如 Mac 版本,点击下一步安装到 /usr/local/go

# 查看Go版本:

❯ go version
go version go1.19 darwin/arm64

# 配置GOPROXY

  • GOROOT="/usr/local/go" 安装 Go 开发包路径
  • GOPATH="/Users/nining/go" Go 1.14版本开始启用Go Moudule模式后,不一定非要将代码写到GOPATH目录下,保持默认值即可
  • 配置 GOPROXY 国内可以访问
go env -w GOPROXY=https://goproxy.cn,direct

# VSCode安装Go插件:

  • Chinese(简体中文)
  • Go(Rich Go language support for Visual Studio Code)
  • IntelliJ IDEA Keybindings (快捷键)
  • 点右下角 Install All

image-20211226102030087

# 创建第一个Go程序

  1. 新建项目目录 hello

进到目录中编写一个main.go文件

执行初始化命令:

go mod init hello
  • hello是你的项目名
  • 只需要在创建项目的时候在项目的根目录下执行一次!
  1. 一个可执行的main.go程序
package main  // 声明 main 包,表明当前是一个可执行程序

import "fmt"  // 导入内置 fmt 包

func main(){  // main函数,是程序执行的入口
	fmt.Println("Hello World!")  // 在终端打印 Hello World!
}
  1. 编译可执行程序

在项目的根目录下执行

go build

指定编译后的文件名称

go build -o xxx

# 多个go文件

一个go项目下可以存在多个go文件。

此时,如果使用go run 执行,那么就需要把所有的源文件都带上。

go run const.go int.go hello.go

否则就容易出现以下问题:

image-20211226145133741

❯ go run hello.go
# command-line-arguments
./hello.go:63:14: undefined: c4
./hello.go:64:14: undefined: d3
./hello.go:64:18: undefined: d4

无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!

无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!

无论是VsCode还是Goland 推荐大家一个窗口打开一个项目!

# 变量 var

变量(Variable)的功能是存储数据

  • Go语言中每一个变量都有自己的类型,并且变量必须经过声明才能开始使用
  • Go语言中同一作用域内不支持重复声明,并且Go语言的变量声明后必须使用
package main

import "fmt"

var version string
var age11 = 18

// name12 := "小王子"  // 函数外语句必须以关键字开头

func main() {
	fmt.Println("Hello world!")

	/*
		多行注释
	*/
	// 变量的声明
	var name string // 声明变量

	// 批量声明
	var (
		age  int  // 0
		isOk bool // false
	)
	// var age int
	// var isOk bool

	age = 100 // 变量赋值
	fmt.Println(name, age, isOk)

	var age2 int = 18 // 声明变量并赋值
	fmt.Println(age2)

	// 没有指定类型?类型推导
	var name3, age3 = "linda", 28

	// var (
	// 	name3 string = "linda"
	// 	age3 int = 28
	// )

	fmt.Println(name3, age3)

	var age4 int8 = 28 // 如果不想用编译器推导的类型,就需要显式指定变量的类型
	fmt.Println(age4)

	// 双引号表示字符串,单引号表示字符
	var x byte = 'a'   // 字符
	var s string = "a" // 字符串
	fmt.Println(x, s)

	// 短变量声明
	s2 := "linda" // var s2 string s2="linda"
	fmt.Println(s2)
	s2 = "小王子"
	fmt.Println(s2)

	// var x2 string
	// x2 = 18 // 只能给变量赋正确类型的值
	// fmt.Println(x2)
}
  • 每个变量会被初始化成其类型的默认值。整型和浮点型变量的默认值为0,字符串变量的默认值为空字符串, 布尔型变量默认为false,切片、函数、指针变量的默认为nil
  • 函数外的每个语句都必须以关键字开始(var、const、func等),也就是 :=不能使用在函数外
  • 匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明,_多用于占位,表示忽略值

# 常量 const

相对于变量,常量是恒定不变的值,常量在定义的时候必须赋值

// const.go

package main

const pi = 3.14

// v v2 v3 v4都是相同的值
const (
	v = "v1.0"
	v2
	v3
	v4
)

const (
	week1 = 1
	week2 = 2
	week3 = 3
)

const (
	n1 = iota // 0
	n2        // 1
	n3
	n4
	n5
)

const (
	z1 = iota // 0
)

// 利用iota声明存储的单位常量
const (
	_  = iota             // 0
	KB = 1 << (10 * iota) // 1<<10 <=> 10000000000
	MB = 1 << (10 * iota) // 1<<20
	GB = 1 << (10 * iota) // 1<<30
	TB = 1 << (10 * iota)
	PB = 1 << (10 * iota)
)

// 声明中插队
const (
	c1 = iota // 0
	c2        // 1
	c3 = 100  // 插队
	c4 = iota // 3
)

const (
	d1, d2 = iota + 1, iota + 2 // 1,2
	d3, d4 = iota + 1, iota + 2 // 2,3
)

iota是Go语言的常量计数器

  • iota在const关键字出现时将被重置为0
  • const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引,即使iota没出现在那一行也会加1)

# 基本数据类型

# 整型

整型分为以下两个大类:

  • 按长度分为:int8、int16、int32、int64
  • 对应的无符号整型:uint8、uint16、uint32、uint64
  • 另外uint8就是byte型,unit和int根据平台32位和64位不同而不同
var (
	i1 int8   = 0b1001  // 二进制
	i2 uint64 = 0o644   // 八进制
	i3        = 0x123   // 十六进制 默认为int
	i4 int32  = 123_456 // _分隔让数字更直观
)

// 利用fmt.Printf 格式化打印
v11 := 123
fmt.Println("自带换行")
fmt.Printf("十进制:%d \n", v11)
fmt.Printf("二进制:%b\n", v11)
fmt.Printf("八进制:%o\n", v11)
fmt.Printf("十六进制:%x\n", v11)

# 浮点型

  • Go语言支持两种浮点型数:float32float64 计算机中浮点数都是不精确的!

计算机中浮点数都是不精确的!

计算机中浮点数都是不精确的!

实际写业务遇到浮点数运算都是转成整型来计算的

func f1() {
	fmt.Printf("%.2f\n", math.MaxFloat32)
}

# 布尔型

Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true(真)false(假)两个值

var b11 = true
var b12 bool // false

注意:

  1. 布尔类型变量的默认值为false
  2. Go 语言中不允许将整型强制转换为布尔型.
  3. 布尔型无法参与数值运算,也无法与其他类型进行转换。

# 字符串

Go语言里的字符串的内部实现使用UTF-8编码,字符串的值为双引号(")中的内容,多行时反引号(`)

package main

import (
	"fmt"
	"strings"
)

func f2() {
	// filename 表示windows下一个文件路径
	filename := "C:\\go\\hello\\hello.exe"
	fmt.Println(filename)

	s11 := "永远不要高估自己"
	fmt.Println(s11)

	s12 := "\"永远不要高估自己\""
	fmt.Println(s12)

	// 多行字符串
	s13 := `多行
字符串
	测\n试
	`
	fmt.Println(s13)

	// 字符串操作
	fmt.Println(len(s11))
	// 字符串拼接
	name1 := "linda"
	value1 := "过年好"
	fmt.Println(name1 + value1)

	ret := fmt.Sprintf("大家好,%s祝大家%s", name1, value1)
	fmt.Println(ret)

	// strings
	s14 := "你:好:呀"
	fmt.Println(strings.Split(s14, ":"))

	fmt.Println(strings.Contains(s14, "你"))
	fmt.Println(strings.HasPrefix(s14, "你:")) // true
	fmt.Println(strings.HasSuffix(s14, "啊"))  // false

	fmt.Println(strings.Index(s14, ":"))     // 3
	fmt.Println(strings.LastIndex(s14, ":")) // 7

	// 拼接
	slice1 := []string{"你", "我", "他"}
	fmt.Println(strings.Join(slice1, "-"))

	// 字符和字符串
	y1 := '中' // 字符
	y2 := "中" // 字符串
	fmt.Println(y1, y2)

	// byte 和rune
	fmt.Println([]rune(s14))
	fmt.Println([]byte(s14))
	// for range循环
	idx := 0
	for _, r := range s14 { // rune表示一个汉字
		if r == ':' {
			fmt.Println(idx)
			break
		}
		idx++
	}
}

  • len("你好") --> 6

# 字符

组成每个字符串的元素叫做"字符",可以通过遍历或者单个获取字符串元素获得字符,Go 语言的字符有以下两种:

  • uint8类型,或者叫byte型,代表一个ASCII码字符
  • int32类型,或者叫rune类型,代表一个 UTF-8字符

byte: 常见的a、b、c等字符

rune: 是用来表示中文、日文等复合字符的

// 遍历字符串
func traversalString() {
	s := "hello世界"
	for i := 0; i < len(s); i++ { //byte
		fmt.Printf("%v(%c) ", s[i], s[i])
	}
	fmt.Println()
	for _, r := range s { //rune
		fmt.Printf("%v(%c) ", r, r)
	}
	fmt.Println()
}

要修改字符串,需要先将其转换成[]rune[]byte,完成后再转换为string,无论哪种转换,都会重新分配内存,并复制字节数组

func changeString() {
	s1 := "big"
	// 强制类型转换
	byteS1 := []byte(s1)
	byteS1[0] = 'p'
	fmt.Println(string(byteS1))

	s2 := "白萝卜"
	runeS2 := []rune(s2)
	runeS2[0] = '红'
	fmt.Println(string(runeS2))
}

# 类型转换

只能在两个类型之间支持相互转换的时候使用

// 类型转换
// T()

func f3() {
	var i11 int8 = 1

	i12 := int64(i11)             // int8 -> int64
	fmt.Printf("i12: %T \n", i12) // int64

	f11 := 12.34                  // float64
	f12 := int64(f11)             // float64 -> int64
	fmt.Printf("f12: %T \n", f12) // int64

	// bool(1) // int -> bool 🚫
}

func sqrtDemo() {
	var a, b = 3, 4
	var c int
	// math.Sqrt()接收的参数是float64类型,需要强制转换
	c = int(math.Sqrt(float64(a*a + b*b)))
	fmt.Println(c)
}

# 五种运算符

五种运算符

  1. 算术运算符 + - * / %
  2. 关系运算符 == != > >= < <=
  3. 逻辑运算符 && || !
  4. 位运算符 位运算符对整数在内存中的二进制位进行操作
    • & 参与运算的两数各对应的二进位相与,(两位均为1才为1)
    • | 参与运算的两数各对应的二进位相或,(两位有一个为1就为1)
    • ^ 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1,(两位不一样则为1)
    • << 左移n位就是乘以2的n次方,a<<b 是把a的各二进位全部左移b位,高位丢弃,低位补0
    • >> 右移n位就是除以2的n次方,a>>b 是把a的各二进位全部右移b位
  5. 赋值运算符 = += /= ...

注意:++(自增)和--(自减)在Go语言中是单独的语句,并不是运算符

# 补充问题:

很多很多个数字中,除了某个数字只出现一次外,其他数字均出现了两次。问如何找出只出现一次的数字?

// 一堆数找出只出现一次的那个
func f11() {
	nums := []int{17, 4, 3, 3, 9, 11, 9, 11, 17}
	if len(nums)%2 == 0 {
		return
	}
	ret := nums[0]
	for _, num := range nums[1:] {
		ret ^= num // 异或
	}
	fmt.Println(ret)

}

# 流程控制语句

流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的"经脉"

# if语句

package main

import "fmt"

// if条件判断分支
func f5() {
	score := 89 // 假设从数据库中查询出一个同学的分数
	if score > 90 {
		fmt.Println("A")
	} else if score > 65 {
		fmt.Println("勉强留下")
	} else {
		fmt.Println("明年再来")
	}
	fmt.Println(score)
}

func f6() {
	// score只在if分支中有效
	// 因为它只在if分支中声明了score,外部不可见
	if score := 89; score > 90 {
		fmt.Println("A")
	} else if score > 65 {
		fmt.Println(score)
		fmt.Println("勉强留下")
	} else {
		fmt.Println("明年再来")
	}
	// fmt.Println(score)
}

# for语句

package main

import "fmt"

// for循环

func f7() {
	// 1.标准for循环
	for i := 0; i <= 10; i++ {
		fmt.Println(i) // 0 1 2 ... 10
	}
	// fmt.Println(i) // 不可访问i

	// 2.初始语句省略
	i := 0
	for ; i <= 10; i++ {
		fmt.Println(i) // 0 1 2 ... 10
	}
	fmt.Println(i) // ? 11

	// 3.初始语句和结束语句都可以省略
	j := 0
	for j < 10 {  // 类似Python中 while j < 10: pass
		fmt.Println(j)
		j++ // 10
	}
	fmt.Println(j) // ? 10

	// 4. 无限循环
	for {
		if j > 12 {
			break // 跳出循环
		}
		fmt.Println("...")
		j++
	}

	// for range 循环
	s := "golang"
	for i, v := range s {
		fmt.Printf("%v:%c \n", i, v)
	}
}

Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)

  • 数组、切片、字符串返回索引和值
  • map返回键和值
  • 通道(channel)只返回通道内的值

# switch语句

package main

import "fmt"

// switch

func f8() {
	finger := 3 // 从外界获取的一个值
	switch finger {
	case 1:
		fmt.Println("大拇指")
	case 2:
		fmt.Println("食指")
	case 3:
		fmt.Println("🖕🏻")
	case 4:
		fmt.Println("无名指")
	case 5:
		fmt.Println("小拇指")
	default:
		fmt.Println("无效的输入")
	}

	num := 9
	switch num {
	case 1, 3, 5, 7, 9:
		fmt.Println("奇数")
	case 2, 4, 6, 8:
		fmt.Println("偶数")
	}

	switch {
	case num%2 != 0:
		fmt.Println("奇数")
	case num%2 == 0:
		fmt.Println("偶数")
	default:
		fmt.Println("num=0")
	}
}

  • 每个switch只能有一个default分支
  • 一个分支可以有多个值,多个case值中间使用英文逗号分隔
  • fallthrough语法可以执行满足条件的case的下一个case,即满足上下两个case

# goto语句

package main

import "fmt"

// goto 语句

func gotoDemo1() {
	var breakFlag bool
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 {
				// 设置退出标签
				breakFlag = true
				break
			}
			fmt.Printf("%v-%v\n", i, j)
		}
		// 外层for循环判断
		if breakFlag {
			break
		}
	}
}

func gotoDemo2() {
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 2 { // 退出整个两层for循环
				goto breakLabel
			}
			fmt.Printf("%v-%v\n", i, j)
		}
	}
breakLabel:
	//
}

# continue语句

package main

import "fmt"

func f9() {
	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			continue // 结束本轮循环,继续下一次循环
		}
		fmt.Println(i)
	}
}

# 课后作业

九九乘法表

package main

import "fmt"

// 九九乘法表

func f10() {
	for i := 1; i < 10; i++ {
		for j := i; j < 10; j++ {
			fmt.Printf("%d*%d=%d\t", j, i, j*i)
		}
		fmt.Println()
	}

	for i := 1; i < 10; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%d*%d=%d\t", j, i, j*i)
		}
		fmt.Println()
	}

}
上次更新: 9/10/2022, 5:57:39 PM