Jochen的golang小抄-皮毛篇

本系列写的是学习过程中的纯代码笔记记录,该系列为代码流,基本只写代码,代码开始前会有一段导读注释,建议先看注释在学习和练习代码

小抄系列主要分为皮毛、基础和进阶篇,本篇章为皮毛,主要讲的是编程语言中首先必须知道的基础组成部分、简单数据类型及程序流程结构的知识

变量 (variable)


part1:

package main

import "fmt"

func main() {
	/*
		变量:variable
		概念:一小块内存,用于村数数据,在程序运行过程中数值可以改变

		使用:
			step1:变量的声明,也叫定义
				第一种:var 变量名 数据类型
						变量名 = 赋值
						var 变量名 数据类型 = 赋值

				第二种:类型推断 省略数据类型
						var 变量名 = 赋值

				第三种 简短声明
						变量名 := 赋值

			step2:变量的访问,数值和取值
					直接变量名访问

		go的特性:
			静态语言:强类型语言
	*/
	//第一种:定义变量, 然后进行赋值
	var num1 int
	num1 = 1
	fmt.Printf("num1等于 %d\n", num1)

	//写在一行
	var num2 int = 2
	fmt.Printf("num2等于 %d\n", num2)

	//第二种:类型推断
	var name = "小黄"
	fmt.Printf("类型是:%T, 数值是:%s\n", name, name)

	//第三种:简约定于,也叫简短声明
	sum := 100
	fmt.Println(sum)

	//多个变量同时定义
	var a, b, c int
	a = 1
	b = 2
	c = 3
	fmt.Println(a, b, c)

	var x, y = 1, 2
	fmt.Println(x, y)

	var (
		name1 = "小明"
		age   = 1
		sex   = "男"
	)
	fmt.Printf("学生姓名:%s,年龄:%d,性别:%s", name1, age, sex)
}

part2:

package main

import "fmt"

var a = 1
//b := 2 //语法错误

func main() {
	/*
		注意点:
		1.变量必须先定义才能使用
		2.变量的类型和赋值必须一致
		3.同一作用域内,变量名不能冲突
		4.简短定义方式,左边的变量至少有一个是新的
		5.简短定义方式,不能定义全局变量(定义在函数外面的变量,包内所有函数都可以访问)
		6.变量的零值,也叫默认值
		7.变量定义了就要使用,否则无法通过编译
	*/
	var num int = 100
	fmt.Printf("num等于%d,地址是%p\n", num, &num)

	num = 200
	fmt.Printf("num等于%d,地址是%p\n", num, &num) // 发现改变数值并没有改变地址

	//fmt.Println(num2) //undefined: num2

	var name string
	//name = 1 //cannot use 1 (type untyped int) as type string in assignment
	name = "jochen"
	fmt.Println(name)

	//var name string = "大黄" //name redeclared in this block
	//fmt.Println(name)

	//num, name := 100, "大黄" //no new variables on left side of :=
	num, name, sex := 100, "大黄", "男" //至少要有一个新的变量被声明
	fmt.Println(num, name, sex)

	//访问全局变量
	fmt.Println(a)

	var x int
	fmt.Println(x) //整型默认存储0
	var y  float64
	fmt.Println(y) //默认存储0
	var s string
	fmt.Println(s) //默认“”
	var s2 []int //切片[]
	fmt.Println(s2)
	fmt.Println(s2 == nil) // s2是否等于空 --这里结果是

}

常量(constant)


package main

import "fmt"

func main() {
	/*
		常量:
			1.概念:同变量类似,但执行过程中数值不能改变
			2.语法:
				显式类型定义:const b string = "jochen"
				隐式类型定义:const b  = "jochen"
			3.常数
				固定的数值:100,"jochen"

			4.注意事项:
				常量中的数据类型之可以是bool、数值型和string
				未被使用的常量在编译的时候不会报错
				显式指定类型的时候,必须确保常量左右值一致
	*/
	//常数
	fmt.Println(100)
	fmt.Println("hello")

	//1.定义常量
	//变量定义以后要使用否则会报错,但是常量不会
	const PI1 float64 = 3.1415926
	const PI2 = 3.1415926

	//2.尝试改变常量的值
	//PI1 = 1.1 // cannot assign to PI1

	//3.定义一组常量
	const C1, C2, C3 = 1, 2, "jochen"
	const (
		NAME = "jochen"
		AGE  = 18
		SEX  = "boy"
	)

	//4.一组变量中,如果某个变量没有初始值,默认和上一行一致
	const (
		a = 100
		b
	)
	fmt.Printf("a的类型是:%T,数值是%d\n", a, a) //a的类型是:int,数值是100
	fmt.Printf("b的类型是:%T,数值是%d\n", b, b) //b的类型是:int,数值是100

}

iota关键字


package main

import "fmt"

func main() {
	/*
		iota关键字
			1.概念:特殊的常量,可以认为好似一个可以被编译器修改的常量
			2.原理:每当定义一个const,iota的初始值为0,
					每当定义一个常量,就会累加1,直到下一个const出现,清零
					const指的是关键字,这里说的定义const是定义一个常量组
	*/
	const (
		a = iota // 0
		b = iota // 1
		c = iota // 2
		d = iota // 3
	)
	fmt.Println(a, b, c, d) //0 1 2 3

	const (
		f = iota // 0
		g        // 不赋值和上一行一致,g=iota -》1
	)
	fmt.Println(f, g) //0,1(清零开始)

	//枚举中,枚举就是一组常量
	const (
		MALE = iota
		FEMALE
		UNKNOW
	)
	fmt.Println(MALE, FEMALE, UNKNOW) //0 1 2

}

小练习看iota

package main

import "fmt"

func main() {
	const (
		A = iota     // 0
		B            // 1
		C            // 2
		D = "JOCHEN" //JOCHEN iota = 3
		E            //JOCHEN iota = 4
		F = 100      //iota = 5
		G            //100 iota = 6
		H = iota     //7
		I            //iota = 8
	)

	const (
		J = iota // 0
	)

	fmt.Println(A)
	fmt.Println(B)
	fmt.Println(C)
	fmt.Println(D)
	fmt.Println(E)
	fmt.Println(F)
	fmt.Println(G)
	fmt.Println(H)
	fmt.Println(I)
}

基本数据类型


布尔类型与数值类型

package main

import "fmt"

func main() {
	/*
		Go语言数据类型:
			1.基本数据类型
				- 布尔类型(bool):
					取值:true, false
				- 数值类型:
					整数:int
						有符号:最高位表示符号位,0正数,1负数,其余表示数值
							int8:-128 ~ 127
							int16:-32768 ~ 32767
							int32:-2147483648 ~ 2147483647
							int64:-9223372036854775808 ~ 9223372036854775807
						无符号:所有的位表示数值
							uint8:0 ~ 255
							uint16:0 ~ 65535
							uint32:0 ~ 4294967295
							uint64:0 ~ 18446744073709551615

						int, uint一般我们使用的时候都是用int或uint
							其范围根据操作系统位数决定是32位还是64位的整数

						byte:可以理解为uint8的别称,它们是可以相互转化的

						rune:可以理解为int32的别称,它们是可以相互转化的

					浮点数:生活中的小数
						float32,float64 两者取值范围不一样罢了
					复数:complex(用的较少,可少关注)
				字符串(string)
			2.复合数据类型
				array, slice, map, function, pointer, struct, interface, channel...
	*/

	//1.布尔类型
	var b1 bool = true
	fmt.Printf("%T, %t\n", b1, b1) //bool, true
	b2 := false
	fmt.Printf("%T, %t\n", b2, b2) //bool, false

	//2.整数
	var i1 int8
	i1 = 10
	fmt.Println(i1) //100
	//i1 = 200        //constant 200 overflows int8 存储范围不够存就报这个错咯

	var i2 uint8 = 200
	fmt.Println(i2) //200 无符号整型可以存0-255,和有符号的差别是最高位是否用来做符号位

	var i3 int = 1000
	fmt.Println(i3)
	//语法角度:int,int64不认为是一种类型
	//var i4 int64 = 2000
	//i4 = i3 //cannot use i3 (type int) as type int64 in assignment 拉闸

	var i5 uint8 = 10
	var i6 byte = 1
	i6 = i5 //可以互换
	fmt.Println(i6)

	var i7 int32 = 10
	var i8 rune = 1
	i8 = i7 //可以互换
	fmt.Println(i8)

	//3.浮点数
	var f1 float32 = 3.14
	var f2 float64 = 6.28
	fmt.Printf("%T, %f\n",f1,f1) //float32, 3.140000
	fmt.Printf("%T, %f\n",f2,f2) //float64, 6.280000

}

字符串(string)

package main

import "fmt"

func main() {
	/*
		字符串:
			1.概念:多个byte的集合,理解为一个字符序列

			2.语法:使用双引号 如:“Jochen”
					也可以使用``,表示原生字符串,可忽略转义字符

			3.原理:就是一连串固定长度的字符串连接起来的字符序列,go的字符序列是由
		单个字节链接起来的。其字节使用UTF-8编码标识Unicode文本

			4.编码问题
				计算机本质只识别0和1这种二进制
				所以字符实际上是数字的编码表,即1个数字对应一个字符
				如:A:65, B:66, C:67, a:97, b:98...
				上述简单字符编码表就是ASCII(美国标准信息交换码)

				ASCII码只能表示8位二进制数的字符,即255个字符,显然像中国汉字是不够用的
				所以有了中国的编码表:gbk,兼容ASCII

				中国编码显然只适用中国和一些以英语为母语的国家,别的国家的也有自己的字符编码
				问题就来了,每个国家只能使用自己的编码,无法跨国协作咯
				为此,某国际组织把全世界的编码都排了一下,于是有了Unicode编码,也被称为万国码

				Unicode只规定了字符对应的编码是多少,但是并没有规定字符的存储方式,即其需要占用多少个byte
				所以Unicode就有了多种实现,常见的有:
					UTF-8:变长编码,ASCII字符集就使用1个字节存储位置,汉字使用3个字节存储,某些字符使用2位字节
					UTF-16,UTF-32...
					PS:Unicode编码不兼容gbk编码,其把中文编码重新编排了,但兼容ASCII

			5.转义字符 \ ,其有两中作用
				a.有一些字符有特殊的作用如:' "单引号双引号,可以转义为普通字符,让其不起作用
				b.本身有一些字符就是一个普通字符,通过转义就有特殊作用了
					比如用的最多的\n,表示的就是换行  \t表示制表符

	*/

	//1.定义字符串
	var str string = "Jochen"
	fmt.Printf("%T, %s\n", str, str) //string, Jochen

	str2 := "Hello Jochen"
	fmt.Printf("%T, %s\n", str2, str2) //string, Hello Jochen

	//2.区别:'A',"A"
	//使用%d可以打印字符编码,%c打印字符,这是格式化动词规则,记住就行,纠结个毛
	s1 := 'A'
	s2 := "A"
	fmt.Printf("%T, %c, %d\n", s1, s1, s1) //int32, A, 65
	fmt.Printf("%T, %s\n", s2, s2)         //string, A

	s3 := '黄'
	fmt.Printf("%T, %c, %d\n", s3, s3, s3) //int32, 黄, 40644

	//3.转义字符
	fmt.Println("\"Jochen\"") //"Jochen"

	//4.原生字符输出 就是所有特殊字符,转义字符在``里面都不好使了
	fmt.Println(`"Jochen\n"`) //"Jochen\n"

}

数据类型转换

package main

import "fmt"

func main() {
	/*
		数据类型转换:Type Convert
		0.前提:Go是静态语言,要求定义、赋值、运算必须类型一致
		1.语法格式:
			Type(Value)
		2.转换场景:
			常数可以在需要的时候,自动转型
			变量需要手动转型 T(V)
	*/

	//强类型语言特点,不同类型的变量不允许相互赋值,即使同为数值类型
	var num1 int8 = 10
	var num2 int16 = 16
	//交换赋值
	//cannot use num2 (type int16) as type int8 in assignment
	//cannot use num1 (type int8) as type int16 in assignment
	//num1, num2 = num2, num1

	//go允许在一定程度做数据类型转换
	//num1 = int8(num2) //大转小可能会有精度损失
	//num2 = int16(num1) //小转大属于向上转型,是安全的操作
	num1, num2 = int8(num2), int16(num1)
	//交换后
	fmt.Println(num1, num2) //16 10

	//不同类别的数据类型是不能转换的 如bool<=>数值
	//b1 :=true
	//num1 = int8(b1) //cannot convert b1 (type bool) to type int8
	//b1 = bool(num1) //cannot convert num1 (type int8) to type bool

	str := '1'
	//num1 = str	//annot use str (type rune) as type int8 in assignment
	//可以看出字符实际是rune类型,其是int3别名,所以直接使用int16可以去接收其值
	var num3 int32 = 32
	num3 = str
	fmt.Println(num3) //49

	//常数可以自动转型
	var f1 float32 = 3.14
	sum := f1 + 100 //100是int64类型,其会自动转型为float32与其进行运算
	fmt.Println(sum) //103.14

	//所以转型是针对变量来说的,常数在有需要的时候可以自动转型
}

算术运算符


算术运算符

package main

import "fmt"

func main() {
	/*
		0.表达式:操作数和表达式组成的序列 如:(a + b) * c
			操作数:a, b, c叫做操作数
			运算符:表达式中的 +,* 叫做运算符
		1.go语言中运算符种类:
			- 算术运算符
			- 关系运算符
			- 逻辑运算符
			- 位运算符
			- 赋值运算符

	*/

	//算术运算符:+, -, *, /, %, ++, --
	num1 := 1
	num2 := 2
	sum := num1 + num2
	fmt.Printf("%d + %d = %d\n", num1, num2, sum) //1 + 2 = 3

	sub := num1 - num2
	fmt.Printf("%d - %d = %d\n", num1, num2, sub) //2 - 1 = -1

	mul := num1 * num2
	fmt.Printf("%d * %d = %d\n", num1, num2, mul) //1 * 2 = 2

	//除号/ 实际上是取商,即取整数部分
	num3 := 10
	num4 := 3
	div := num3 / num4                            //取商
	fmt.Printf("%d / %d = %d\n", num3, num4, div) //10 / 3 = 3

	//取余符号为%  取的是余数部分
	mod := num3 % num4                             //取余
	fmt.Printf("%d %% %d = %d\n", num3, num4, mod) //10 % 3 = 1

	c := 3
	c++            //就是给c+1
	fmt.Println(c) //4

	c--            //就是给c-1
	fmt.Println(c) //3
	/*
		注意:
			- go中 ++ 和 -- 不能在表达式中参加运算,只能是单独的表达式java是支持的
			- go中没有--num 和 ++num的操作
	*/
}

关系运算符

package main

import "fmt"

func main() {
	/*
		关系运算符:>, <, >=, <=, ==, !=
			结果总是布尔类型:true or false
			==表示比较两个数值是相等的
			--表示比较两个数值不相等
	*/

	a := 1
	b := 2
	c := 1
	res1 := a > b
	res2 := a < b
	res3 := a == c
	res4 := a != b
	fmt.Printf("%d > %d => %t\n", a, b, res1)  //1 > 2 => false
	fmt.Printf("%d < %d => %t\n", a, b, res2)  //1 < 2 => true
	fmt.Printf("%d == %d => %t\n", a, c, res3) //1 == 1 => true
	fmt.Printf("%d != %d => %t\n", a, b, res4) //1 != 2 => true

}

逻辑运算符

package main

import "fmt"

func main() {
	/*
		逻辑运算符:&&, ||, !
			操作数为布尔类型
			运算结果也为布尔类型

		&&:逻辑与,两个操作数都非假,则为真
			“一假则假,全真为真”
		||:逻辑或,任意一个操作数是非假,则为真
			“一真则真,全假则假”
		!:逻辑非,真则假,假则真
	*/

	a := 2 > 1  //true
	b := 1 < 2  //false
	c := 1 == 1 //true
	res1 := a && b
	res2 := a && b && c
	res3 := a || b
	res4 := !a
	res5 := !b
	fmt.Printf("%t && %t => %t\n", a, b, res1)          //true && false => false
	fmt.Printf("%t && %t && %t => %t\n", a, b, c, res2) //true && false && true => false
	fmt.Printf("%t || %t => %t\n", a, c, res3)          //true || true => true
	fmt.Printf("!%t => %t\n", a, res4)                  //!true => false
	fmt.Printf("!%t => %t\n", b, res5)                  //!false => true

}

注意:&&和||都是短路运算符,即&&做运算时前面的运算遇到为假,则后面不做运算,直接得出假。||则反之,遇到真,后面不做运算,直接得出真

位运算符

package main

import "fmt"

func main() {
	/*
		位运算符:
			将数值,转换为二进制后,按位操作
		按位与 &
			对应位的值如果都为1才为1,有一个为0则为0
		按位或 |
			对应位的值如果都是0才为0,有一个为1就为1
		异或  ^
			二元:a^b
				对应位的值不同为1,相同为0
			一元:^a
				按位取反 0-->1  1-->0
		(go特有)位清空 &^:
			对于 a &^ b
				对于b上的每个二进制位
				如果为0,则取a对应位上的数值
				如果为1,则结果位就取0
		位移运算符:
			<< 按位左移: a<<b
				将a转为二进制,向左移动b位

			>> 按位右移: a<<b
			将a转为二进制位,向右移动b位
	*/

	a := 60
	b := 13
	fmt.Printf("a:%d, 二进制:%b\n", a, a) //a:60, 二进制:111100
	fmt.Printf("b:%d, 二进制:%b\n", b, b) //b:13, 二进制: 1101
	/*
		人工算:
			60  0011 1100
			13  0000 1101
			&	0000 1100
			|   0011 1101
			^	0011 0010
			&^	0011 0000
	*/

	// &
	res1 := a & b
	fmt.Printf("%08b & %08b = %08b\n", a, b, res1) //00111100 & 00001101 = 00001100

	// |
	res2 := a | b
	fmt.Printf("%08b | %08b = %08b\n", a, b, res2) //00111100 | 00001101 =  00111101

	// ^
	res3 := a ^ b
	fmt.Printf("%08b ^ %08b = %08b\n", a, b, res3) //00111100 ^ 00001101 = 00110001

	res4 := ^a                            //按位取反
	fmt.Printf("^%08b = %08b\n", a, res4) // ^00001101 = -0111101

	// &^
	res5 := a &^ b
	fmt.Printf("%08b &^ %08b = %08b\n", a, b, res5) //00111100 &^ 00001101 = 00110000

	c := 8
	res6 := c << 2
	/*
		c : ... 0000 1000
				0010 0000  低位补零
	*/
	fmt.Printf("%08b << 2 = %08b\n", c, res6) //00001000 << 2 = 00100000

	res7 := c >> 3
	/*
		c : ... 0000 1000
				0000 0001  高位补零
	*/
	fmt.Printf("%08b >> 3 = %08b\n", c, res7) //00001000 << 3 = 00000001

}

赋值运算符

package main

import "fmt"

func main() {
	/*
		赋值运算符
			=, +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^=
			= 把=右侧的数值赋值给左侧的变量

			其他运算符都是在=拓展出来的,上面与=搭配的左侧运算符在前面都介绍过,这里只举一个例子
			a += b => a = a + b 这二者是一样的,可以认为是简写
			so:  a %= b => a = a % b
	*/
	var i int = 8
	fmt.Println(i) // 8

	a := 1
	b := 2
	a += b
	fmt.Printf("a = %d\n", a) // 3

	c := 1
	d := 2
	c = c + d
	fmt.Printf("c = %d\n", c) // 3

}

运算符的优先级(级别高优先)

优先级 operators
7 (单目运算符)~ ! ++ --
6 * / % << >> & &^^
5 + - ^
4 == != < <= >= >
3 (通道操作,后续介绍)<-
2 &&
1 ||

记不住?没问题,你在程序中写一连串鸡肠似的一连串表达式,谁愿意去看,我们可以使用()括号来临时提升某个表达式的整体运算级别(最高级别)

键盘输入和打印输出(到屏幕)

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	/*
		输入和输出
		fmt包:输入,输出

		输出:
			Print()		//打印
			Printf()	//格式化打印
			Println()	//打印之后换行

		格式化打印占位符,常用的占位符有如下:
		general:
			%v: 原样输出
			%T:	打印数据类型
			%%:原样输出%

		boolean:
			%t: 打印布尔类型

		interger:
			%b: 基于二进制打印
			%c: 打印数值所对应的字符
			%d: 打印十进制数值
			%o: 打印把禁止数值
			%q	该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
			%x	表示为十六进制,使用a-f
			%X	表示为十六进制,使用A-F
			%U	表示为Unicode格式:U+1234,等价于"U+%04X"

		float:
			%b	无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
			%e	科学计数法,如-1234.456e+78
			%E	科学计数法,如-1234.456E+78
			%f	有小数部分但无指数部分,如123.456
			%F	等价于%f
			%g	根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
			%G	根据实际情况采用%E或%F格式(以获得更简洁、准确的输出

		string & byte:
			%s	直接输出字符串或者[]byte
			%q	该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
			%x	每个字节用两字符十六进制数表示(使用a-f)
			%X	每个字节用两字符十六进制数表示(使用A-F)

		point:
			%p	表示为十六进制,并加上前导的0x


		输入:
			Scanln()
			Scanf()
				从字符串str扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。

		拓展:输入还可以使用bufio包
			主要用来操作文件读写,文件读写具体内容后面再介绍,这里用来演示从标准输入中读取
	*/

	//输出
	i := 92                                          //int
	f := 3.14                                        //float64
	b := true                                        //bool
	s := "Jochen"                                    //string
	c := 'A'                                         //char
	fmt.Printf("i => Type:%T, value:%d\n", i, i)     //i => Type:int, value:92
	fmt.Printf("i => Type:%T, charValue:%c\n", i, i) //i => Type:int, charValue:\
	fmt.Printf("f => Type:%T, value:%v\n", f, f)     //f => Type:float64, value:3.14
	fmt.Printf("b => Type:%T, value:%t\n", b, b)     //b => Type:bool, value:true
	fmt.Printf("s => Type:%T, value:%s\n", s, s)     //s => Type:string, value:Jochen

	//单个字符使用''单引号框起来,如果没有显示的指定字符类型的话,其默认是rune类型
	fmt.Printf("c => Type:%T, runevalue:%v\n", c, c) //c => Type:int32, runevalue:65
	//当我们打印rune类型的时候,如果使用格式化%v输出会是code point,如果想输出字符我们应该使用%c(任何整数类型实际上都可以使用%c打印字符,但是使用rune可以明显的表达你的意图)
	fmt.Printf("c => Type:%T, runevalue:%c\n", c, c) //c => Type:int32, runevalue:A

	//输入
	var x int
	var y float64
	fmt.Println("少侠,请拿起你的键盘,敲个整数和浮点类型")
	//字符间使用空白分隔,直到换行停止扫描
	fmt.Scanln(&x, &y) //读取键盘的输入,通过操作地址来赋值给x和y两个变量,什么是地址?后面介绍指针再了解吧
	fmt.Printf("x的值:%d, y的值:%f\n", x, y)

	//格式化输入,根据格式化类型读取字符
	fmt.Scanf("%d,%f", &x, &y) //注意要按照字符串里面的格式去输入,如本示例要输入一个整数和浮点数则100,3.14
	fmt.Printf("x的值:%d, y的值:%f\n", x, y)

	//bufio
	fmt.Println("请输入一个字符串")
	reader := bufio.NewReader(os.Stdin) //从标准输入读取字符
	s1, _ := reader.ReadString('\n')    //直到读取到什么字符结束
	fmt.Println("读到的数据:",s1)
}

程序流程结构


程序的流程结构:

  • 顺序结构:从上向下,逐行执行

  • 选择结构:条件满足,某段代码执行
    分支语句:if,switch,select(通道专用)

  • 循环结构,条件满足,某段代码执行多次

    ​ 循环语句:for

选择结构

if语句

package main

import "fmt"

func main() {
	/*
		if: else if可以有多个
			if 布尔表达式`{
				在布尔表达式为true时执行
			} else if布尔表达式2{
				在上面布尔表达式都为false时执行
			}
			} else{
				在上面所有布尔表达式都为false时执行
			}

		注意事项:go语言中在语法形式上有着严格的规定
			1.if后{,一定是和if条件写在同一行,不能写到下一行
			2.else if和else一定紧跟在是if}之后,不能写到下一行
			3.当else存在的时候,分支语句必选其一执行
	*/
	// 1.给定一个数字,如果大于8,输出该数字,大于16提示这个数大于16并输出该数
	var num int
	fmt.Print("请输入一个数字,按回车键结束:")
	fmt.Scanf("%d", &num)
	//fmt.Scanln(&num)
	//if num > 8 {
	//	fmt.Printf("num:%d\n", num)
	//} else if num > 16 {
	//	// 如果这样写,条件判断是顺序判断,上面的布尔表达式成立,则不会执行这判断,也就是说该代码段永远不会执行
	//	fmt.Printf("num:%d大于16", num)
	//} else {
	//	fmt.Println("数字小于8,不输出")
	//}
	// 正确的方式:
	if num > 16 {
		fmt.Printf("num:%d,大于16", num)
	} else if num > 8 {
		// 如果这样写,条件判断是顺序判断,上面的布尔表达式成立,则不会执行这判断,也就是说该代码段永远不会执行
		fmt.Printf("num:%d\n", num)
	} else {
		fmt.Println("数字小于8,不输出")
	}
}

在go语言中,if语句还有个变形写法,这是在其他语言中没有的:

package main

import "fmt"

func main() {
	/*
		if语句的其他写法 :即把变量初始化在if后面,然后在条件分支语句内部使用,外部是无法使用的
		if 初始化语句; 条件{
		}
	*/
	// if结束后,num在内存中销毁
	if num := 8; num > 0 {
		fmt.Println("整数")
	} else if num == 0 {
		fmt.Println("零蛋")
	} else {
		fmt.Println("负数")
	}

	//fmt.Printf("if初始化语句初始化的变量在条件语句外部无法使用,num:%d", num) //undefined: num
}

switch语句

package main

import "fmt"

func main() {
	/*
		switch语句:
			其是一个条件语句,它计算表达式并将与其可能匹配的列表进行比较,匹配后将执行该代码块
			它被认为是一种惯用的方式来写多个if else语句
		语法结构:
			switch 某个变量(无数据类型限制) {
			case 值1:
				...代码块1
				fallthrough //匹配后强制执行下面一个case
			case 值2:
				...代码块2
			default:
				...代码块3 //如果都没有匹配上执行最后的default,相当于else
			}
		注意:
			1.switch可以作用于其他类型上,case后的数值必须和switch作用的变量类型一致
			2.case是无顺序的
			3.case后的数值是唯一的
			4.在go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case
			5.如想要继续往下执行后续的case,可以使用fallthrough关键字
			6.如果switch没有表达式,它会匹配true
			7.switch后可以多一条初始化语句
				switch 初始化语句;变量{
				}
	*/

	//1.基础用法
	var num int
	fmt.Print("请输入一个数:")
	fmt.Scanln(&num)
	switch num {
	case 1:
		fmt.Println("第一段代码块")
		fallthrough
	case 2:
		fmt.Println("第二段代码块")
	case 3:
		//第一段代码块如果执行,因为fallthrough会强制执行第二块代码块,但不会执行第三块代码块
		fmt.Println("第三段代码块")
	default:
		fmt.Println("所有都不符合就执行这个")
	}

	//模拟计算器
	fmt.Println("------------------------------")
	var num1 float64 = 0
	var num2 float64 = 0
	oper := ""
	fmt.Print("请输入一个计算表达式(操作数和运算符直接需以空格分割):")
	fmt.Scanf("%f %v %f", &num1, &oper, &num2)
	fmt.Println(num1, oper, num2)
	var res float64
	switch oper {
	case "+":
		res = num1 + num2
	case "-":
		res = num1 - num2
	case "*":
		res = num1 * num2
	case "/":
		res = num1 / num2
	case "%":
		temp1, temp2 := int64(num1), int64(num2)
		var res1 = temp1 % temp2 //%运算符只能作用于整型操作数并且得到的也是整型,所以要用强转
		res = float64(res1)
	default:
		res = num1 + num2
	}
	fmt.Printf("%.3f %s %.3f = %.3f\n", num1, oper, num2, res)

	//2.switch省略了后面的变量,相当于匹配bool
	fmt.Println("------------------------------")
	switch {
	case true:
		fmt.Println("jochen")
	case false:
		fmt.Println("这块永远不会被执行")
	}
	/*
		平时也不会像上面那样写,因为毫无意义,省略变量的写法主要结构表达式case使用,如下栗子:
		成绩:
		[0.59] 不及格
		[60,69] 及格
		[70,79] 中
		[80,89] 优秀
		[90,99] 学霸
		100  学神
	*/
	fmt.Println("------------------------------")
	score := 0
	fmt.Scanln(&score)
	switch {
	case score <= 59:
		fmt.Println("不及格")
	case score >= 60 && score <= 69:
		fmt.Println("及格")
	case score >= 70 && score <= 79:
		fmt.Println("中")
	case score >= 80 && score <= 89:
		fmt.Println("优秀")
	case score >= 90 && score <= 99:
		fmt.Println("学霸")
	case score == 100:
		fmt.Println("学神")
	}

	//3.case后可以同时跟随多个数值
	/*
		输入年月,得出该月有多少天
	*/
	fmt.Println("------------------------------")
	month := 0
	day := 0
	year := 0
	fmt.Println("请输入年月,以空格分隔")
	fmt.Scanln(&year, &month)
	switch month {
	case 1, 3, 5, 7, 8, 10, 12:
		day = 30
	case 2:
		if year%400 == 0 || year%4 == 0 && year%100 != 0 {
			day = 29
		} else {
			day = 28
		}
	default:
		fmt.Println("输入的月份有误")
	}
	fmt.Printf("这个月有%v天", day)

	//4.switch后也可以定义变量,该变量只能在switch作用范围内使用
	switch language := "go";language {
	case "go":
		fmt.Println("go语言")
	case "c#":
		fmt.Println(".Net语言")
	case "java":
		fmt.Println("Java语言")
	}
	//fmt.Println(language) //找不到,无法在外部使用switch中定义的变量
}

拓展:break与fallthrough

package main

import "fmt"

func main() {
	/*
		switch中的break和fallthrough语句:
			break:可以使用在switch中,也可以使用在for循环中
				在switch中可以强制结束case语句,从而结束switch分支
			fallthrough:用于穿透switch,相当于在两个case之间起到了连接作用,
						当含有该关键字的case代码段,在匹配后可以穿透到下一个case代码段继续执行
			注意:fallthrough 应该位于某个case的最后一行
	*/

	var num = 1
	switch num {
	case 1:
		fmt.Println(1)
		fmt.Println(1)
		break //用于强制结束case,意味着switch被强制结束,下面的一条输出语句不会被执行
		fmt.Println(1)
	case 2:
		fmt.Println(2)
		fmt.Println(2)
		fmt.Println(2)
	}
	fmt.Println("-----------------------")

	m := 2
	switch m {
	case 1:
		fmt.Println("第一季")
	case 2:
		fmt.Println("第二季")
		fallthrough
	case 3:
		//case 2匹配的话,这里也会被执行
		fmt.Println("第三季")
	case 4:
		//不会穿透到这里,除非case3继续加上fallthrough
		fmt.Println("第四季")
	}

}

循环结构

在go中只有for循环结构,不像其他语言一样有while、 do while和for循环

for循环

package main

import "fmt"

func main() {
	/*
		for循环:某段代码会被多次执行
			1.语法结构:
				for init; condition; post{
					循环体
				}
				init:变量初始化,只执行一次
				condition:bool类型表达式,表示循环的条件
				post:循环体执行之后执行的表达式,一般为变量的变化
			2.其他写法:
				上述for循环的三个组成部分,都是可选的
				- 同时省略init和post,此时相当于其他语言中的while循环
					for condition {
						循环体
					}
				- 省略condition部分,此时就相当于作用在true上,即死循环,在别的语言中为while true 或是for;;
					for {
						循环体
					}
			3.总结:
			for循环省略几个表达式都可以
				省略init:就需要把init定义在外面
				省略condition:则这个循环用真,即死循环
				省略post:变量的变化就需要写在循环里面,否则也是个死循环
	*/

	//1.标准写法
	// 打印3次 hello world
	for i := 0; i < 3; i++ {
		fmt.Println("hello world")
	}
	//for循环中定义的变量作用域只在循环体内,外部无法访问
	//fmt.Println(i) //undefined: i

	//2.等同于别的语言中while循环写法
	i := 0
	for i < 3 {
		fmt.Println("hello world")
		i++
	}
	//该写法和上面的主要区别是,当for循环结束后可以访问变量i的值
	fmt.Println("------------>",i)

	//3.死循环写法 等同于别的语言的while true的写法
	i3 := 0
	for {
		//可以使用break打断循环
		if i3 > 3 {
			break
		}
		fmt.Println("我会被疯狂的无限执行,除非遇到break")
		i3++
	}
}

下面是一些for循环的练习题,我跟着视频练手的,感兴趣的同学也可以跟着练下指法,消除下从别的而语言转过来的不适

part1

package main

import "fmt"

func main() {
	/*
		练习1:打印100-8的数字
		练习2:求0-100的和
		练习3:打印1-100内,能够内3整除,但不能被5整除的数字,统计被打印的数字个数,每行打印五个
	*/

	// 1
	for i := 100; i >= 8; i-- {
		fmt.Println(i)
	}

	//2
	fmt.Println("------------------")
	for sum, i := 0, 0; i <= 100; i++ {
		sum += i
		if i == 100 {
			fmt.Println(sum) //sum应该定义在外部,然后在外部打印比较合适,就用每次循环都需要判断,效率会更高
			//我在这里这样操作一是为了展示,init是可以定义多个变量的,二是为了搞骚操作
		}
	}

	//3
	fmt.Println("------------------")
	count := 0//计数器
	for i := 1; i <= 100; i++ {
		if i%3 == 0 && i%5 != 0 {
			fmt.Print(i,"\t")
			count++
			if count % 5  == 0{
				//每行打印五个,这计数器可以用在两个地方,是不是比妙蛙种子还要妙
				fmt.Println("")
			}
		}
	}
	fmt.Println("\ncount:",count)
}

part2

 package main

import "fmt"

func main() {
	/*
		循环嵌套:多层循环嵌套在一起
		口诀:外层循环控制行,内层循环控制列

		1.打印下面的形状
		*****
		*****
		*****
		*****
		*****

		2.打印99乘法表
	*/
	//1
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			print("*") //打印函数在如今已经变成go的内置函数,可以直接使用,不用调fmt包
		}
		println()
	}

	//2
	for i := 1; i <= 9; i++ {
		for j := 1; j <= i; j++ {
			//按照口诀,外层循环控制行,内层循环控制列,乘法表第一行有1列,第二行有两列...
			//即行与列是i相同的,所以内层j的条件应该是<=i即打印i次,就会有i列
			fmt.Printf("%v x %v = %v\t", i, j, i*j)
		}
		fmt.Println()

	}

}

part3

package main

import (
	"fmt"
	"math"
)

func main() {
	/*
		水仙花数:三位数:[100,999]
			每个位上的数字的立方和,刚好等于该数字本身。那么就叫水仙花数,4个
			比如:153
				1*1*1  +  5*5*5  +  3*3*3  = 1+125+27=153
		利于除取整,模取余的特性:
		268
			获取百位: 268 / 100 =2
			获取个位: 268 % 10  =8

			获取十位: 268 -->26  %10 =6
					或268 -->68  /10 =6
	*/

	for i := 100; i < 1000; i++ {
		x := i / 100     //百位
		y := i / 10 % 10 //个位
		z := i % 10      //个位

		if math.Pow(float64(x), 3)+math.Pow(float64(y), 3)+math.Pow(float64(z), 3) == float64(i) {
			fmt.Printf("水仙花数:%d\n", i)
		}
	}
}

拓展:break和continue

package main

import "fmt"

func main() {
	/*
		循环结束:
			- 循环条件不满足,循环自动结束
			- 可以通过break和continue强制结束整个或者某次循环
		循环控制语句
			break:
				彻底的结束整个for循环
			continue:
				马上结束当前的一次循环,进入当下一次循环中
	*/

	for i := 0; i < 10; i++ {
		if i == 5 {
			break //终止整个for循环
		}
		print(i, "\t")
		/*
			到5生命就到了尽头了
			0       1       2       3       4
		*/
	}
	
	fmt.Println("\n-------------------------------------")
	for i := 0; i < 10; i++ {
		if i == 5 {
			continue //终止i==5的这次循环,下一次循环还会输出
		}
		print(i, "\t")
		/*
			缺少5
			0       1       2       3       4       6       7       8       9
		*/
	}
}

学习资料参考:这里

posted @ 2021-01-12 00:34  .Jochen  阅读(129)  评论(0编辑  收藏  举报