(一)Go语言学习笔记

Go语言学习笔记

  • 1 前言
  • 2 写Go语言需要注意的地方
    • 2.1 Go语言编译执行和直接run的区别
    • 2.2 Go语言的特点
    • 2.3 Linux下配置Go环境变量
    • 2.4 随记
  • 3 go_code
    • 3.1 Variable(变量)
      • 3.1.1 int
      • 3.1.2 float
      • 3.1.3 string
      • 3.1.4 byte
      • 3.1.5 bool
      • 3.1.6 Var Transform(类型转换)
        • 3.1.6.1 string转基本数据类型
        • 3.1.6.2 基本数据类型转string
        • 3.1.6.3 基本数据类型转换
      • 3.1.7 Scanln && Scanf
        • 3.1.7.1 Scanf
        • 3.1.7.2 Scanln
    • 3.2 Escaper Char(转义字符)
    • 3.3 Operator(运算符)
      • 3.3.1 基本运算符
      • 3.3.2 其它运算符
      • 3.3.3 shift(移位运算)
      • 3.3.4 example
      • 3.3.5 attention
    • 3.4 System(进制)
    • 3.5 Pointer(指针)
      • 3.5.1 example
      • 3.5.2 attention
    • 3.6 Controlling(控制语句)
      • 3.6.1 if
      • 3.6.2 for
        • 3.6.2.1 exa1
        • 3.6.2.2 exa2
        • 3.6.2.3 exa3
        • 3.6.2.4 exa4
        • 3.6.2.5 exa5
        • 3.6.2.6 exa6
        • 3.6.2.7 main
        • 3.6.2.8 attention
      • 3.6.3 while
      • 3.6.4 continue
        • 3.6.4.1 exa1
        • 3.6.4.2 exa2
        • 3.6.4.3 main
        • 3.6.4.4 attention
      • 3.6.5 break
        • 3.6.5.1 main
        • 3.6.5.2 exa1
        • 3.6.5.3 label
      • 3.6.6 return
      • 3.6.7 goto
        • 3.6.7.1 example
        • 3.6.7.2 attention

1 前言

本文篇幅过长,仅供参考

B站韩顺平Go语言编程
Go语言标准库中文版
Golang sdk 下载

2 写Go语言需要注意的地方

  • Go语言语句后边不要带分号(它会在每行后自动加分号)
  • Go语言应用程序的执行入口是main()函数
  • Go语言严格区分大小写
  • 由于Go编译器是一行行进行编译的,因此一行就写一条语句,不能把多条语句写在同一行
  • Go语言定义的变量或者import的包如果没有使用到,代码不能编译通过

2.1 Go语言编译执行和直接run的区别

  • 1、如果先编译再执行,那么编译后的二进制可执行文件可以拷贝到没有go开发环境的机器上,仍然可以运行。
  • 2、如果直接run源代码,那么如果这段代码拷贝到其它没有go开发环境的机器上,是无法正常执行的
  • 3、在编译时,编译器会将程序依赖的库文件包含在可执行文件中,所以编译后的可执行文件变大了许多

2.2 Go语言的特点

Go语言保证了既能达到静态编译语言的安全和性能,又达到了动态语言开发和维护的高效率,使用一个表达式来形容Go语言:Go=C + Python

  • 1、Go从C语言中集成了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指针等等,也保留了和C语言一样的编译执行方式及弱化的指针。
  • 2、引入了包的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在。
  • 3、垃圾回收机制gc,内存自动回收,不需要开发人员管理。
  • 4、天然支持并发。
    (1)从语言层面支持并发,实现简单。
    (2)goroutine,轻量级线程,可实现大并发处理,高效利用多核。
    (3)基于CPS并发模型(Communicating Sequential Processes)实现。
  • 5、吸收了管道通信机制,形成Go语言特有的管道channel
  • 6、函数可返回多个值。
  • 7、支持切片、延时执行defer等等。

2.3 Linux下配置Go环境变量

vim /etc/profile
export GOROOT=/opt/go
export PATH=$GOROOT/bin:$PATH
export GOPATH=$HOME/goprojects/

SDK: 软件开发工具包,首先要配置好SDK
GOROOT: 指定SDK的安装目录
GOPATH: GO项目的目录
Path: 指定SDK目录下边的bin目录

单行注释:

//尽量用行注释来注释代码块
//Ctrl +  / 快速注释

块注释:

/*
	...
*/
// 块注释中不允许有块注释嵌套

2.4 随记

为什么需要变量
变量是程序的基本组成单位,其相当于内存中的一个数据存储空间的表示,我们通过变量名可以访问到变量(值)

变量的基本使用:
1、声明变量(定义变量)
2、赋值
3、使用

gofmt -w xxx.go 可以自动对齐代码

3 go_code

3.1 Variable(变量)

3.1.1 int

package main

// import "fmt"
// import "unsafe"

import (
	"fmt"
	"unsafe"
)

func main()  {
	// 有符号整数(int)的范围
	// int8		1bytes	-128~127
	// int16 	2bytes	-2^15~2^15-1
	// int32	4bytes	-2^31~2^31-1
	// ...
	var y int8 = 127 //当y为128时就会报错
	fmt.Println("y=", y)

	// 无符号整数(uint)的范围
	// uint8 	1bytes 	0~255
	// uint16 	2bytes	0~2^15-1
	// uint32 	4bytes	0~2^32-1
	// ...
	var h uint8 = 255
	fmt.Println("h=", h)

	// rune 有符号整数类型 类似int32 表示一个Unicode码
	// byte 无符号整数类型 与uint8等价 当要存储字符时选用byte
	var a int = 9000
	var b uint = 1 
	var c byte = 255
	var d rune = 100
	fmt.Println("a=", a, "b=", b, "c=", c, "d=", d)

	// 查看某个变量的数据类型 fmt.Printf()
	fmt.Printf("a 的类型 %T\n", a)

	// 查看某个变量的字节大小
	fmt.Printf("a 的类型 %T,占用的字节数是 %d", a, unsafe.Sizeof(a))

	// 在Golang中使用变量的时候,遵守保小不保大的原则
	// 即:在保证程序正确运行下,尽量使用占用空间小的数据类型,如年纪age可以使用byte类型
	var age byte = 20
	fmt.Println("age=",age)

	// bit:计算机中的最小存储单位。byte:计算机中基本存储单元 1byte = 8bit
}

3.1.2 float

package main
import (
	"fmt"
)


func main()  {
	var price float32 = 88.88
	fmt.Println("price is ", price)

	// go语言中小数类型分为两大类,单精度float32,双精度float64
	// float32	4bytes
	// float64	8bytes
	// 浮点数=符号位+指数位+尾数位(浮点数都是有符号位的)

	var num1 float32 = -0.00001
	var num2 float64 = -78098238.08
	fmt.Println("num1=",num1, "num2=", num2)
	fmt.Println("num1=",num1, "num2=", num2)

	// 尾数部分可能丢失,造成精度丢失,同样记录一个-123.00000901双精度要比单精度更精确

	// Golang的浮点类型默认声明为float64
	var num3 = 2.2
	fmt.Printf("num3的数据类型为%T\n",num3)

	// 十进制数形式:
	num4 := 5.12
	num5 := .123 // =>0.123
	fmt.Println("num4=",num4, "num5=", num5)
	fmt.Println("num4=",num4, "num5=", num5)

	// 科学计数法形式
	num6 :=5.12345e2  // 5.12345 * 10的2次方	512.345
	num7 :=5.12345E2  // 5.12345 * 10的2次方	512.345
	num8 :=5.12345E-2  // 5.12345 / 10的2次方 0.0512345
	fmt.Println("num6=",num6, "num7=", num7, "num8", num8)

	// 推荐使用float64
}

3.1.3 string

package main
import (
	"fmt"
)

func main()  {
	var address string = "我爱你中国 12345 hello world"
	fmt.Println(address)

	// 字符串一旦赋值内容就不能修改:在go中字符串是不可变的
	// var str = "hello"
	// str[0] = 'a'  这样就会报错

	// 字符串有两种表示形式
	// (1)双引号,会识别转义字符
	// (2)反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果
	str2 := "abc\nabc"
	fmt.Println(str2)

	str3 := `
	afd
	afdf
	af
	asf
	afds
	`
	fmt.Println(str3)

	// 字符串拼接 +
	var str4 = "hello" + "world"
	str4 += " haha!"
	fmt.Println(str4)

	// 当一个拼接的操作很长时,可以分行写,拼接符留在上一行
	var str5 = "hello" + "world" +
	"hello" +
	"world"
	fmt.Println(str5)
}

3.1.4 byte

package main

import (
	"fmt"
)

// 字符串就是一串固定长度的字符连接起来的字符序列
func main()  {
	// Golang中没有专门的字符类型,如果要存储单个(字母),一般使用byte来保存
	// Golang的字符串是由字节组成的
	var c1 byte = 'a'
	var c2 byte = '0'	// 字符的0
	fmt.Println("c1=", c1, "c2=", c2)  // c1= 97 c2= 48 会输出ASCII码值
	fmt.Printf("c1=%c c2=%c\n", c1, c2)  // c1=a c2=0

	// var c3 byte = '京'  // 结果会溢出 overflow
	var c3 int = '京'
	fmt.Printf("c3=%c c3对应的码值=%d\n", c3, c3)  // c3=京 c3对应的码值=20140

	// 如果我们保存的字符在ASCII表中,比如[0-9,a-z,A-Z]可以直接保存到byte
	// 如果我们保存的字符对应码值大于255,这时我们可以考虑使用int类型保存
	// 如果我们需要按照字符的方式输出,需要格式化输出,即 fmt.Printf("%c")

	// utf-8编码包含了ASCII编码,英文字母1个字节,汉字3个字节

	// 在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值

	var c4 int = 22269  // 22269 -> '国'
	fmt.Printf("c4=%c\n", c4)

	// 由于字符类型本质是一个整数,所以其也是可以运算的,运算的时候是按照码值运算
	var n1 = 10 + 'a'
	fmt.Println("n1=", n1)  //n1= 107
}

// 字符型存储到计算机中,需要将字符对应的码值(整数)找出来
// 存储:字符-->对应码值-->二进制-->存储
// 读取:二进制-->码值-->字符-->读取
// Go语言的编码都统一成了utf-8,所以不会有乱码出现

3.1.5 bool

package main

import (
	"fmt"
	"unsafe"
)

func main()  {
	var b = false
	fmt.Println("b=",b)

	// bool类型占用的存储空间是1个字节
	fmt.Println("b 的占用空间 =", unsafe.Sizeof(b))

	// 布尔类型也叫bool类型,只允许其取值true or false
	// 常用在循环语句中
	// bool值默认是false
}

3.1.6 Var Transform(类型转换)

3.1.6.1 string转基本数据类型

package main

import (
	"fmt"
	"strconv"
)

func main()  {
	var str string = "true"
	var b bool

	// 转bool
	// strconv.ParseBool会返回两个值,第一个值是布尔值,第二个值是error值
	// 我们只想获取到布尔值,对err不关心,所以可以用下划线 _ 来忽略掉err
	b , _ = strconv.ParseBool(str)
	fmt.Printf("b type %T b=%v\n",b, b)
	// 输出结果:b type bool b=true

	// 转int
	var str2 string = "123456"
	var n1 int64
	var n2 int
	n1, _ = strconv.ParseInt(str2, 10, 64)
	n2 = int(n1)
	fmt.Printf("n1 type %T n1=%v\n", n1, n1)
	fmt.Printf("n2 type %T n2=%v\n", n2, n2)

	var str3 string = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("f1 type %T f1=%v\n",f1 ,f1)

	// 在String类型转成基本数据类型时,要确保String类型能够转成有效的数据
	// 比如我们可以把"123"转成一个整数,但是不能把“hello”转为一个整数,如果这样Golang会将其转为0
	// 如果n3没有转换成功,则会转成默认值0
	var str4 string = "hello"
	var n4 int64 = 11
	n4, _ = strconv.ParseInt(str4, 10, 64)
	fmt.Printf("n4 type %T n4=%v\n", n4, n4)
	// 输出结果:n4 type int64 n4=0

	// 字符串转换为bool的情况会转为默认false
	var str5 string = "hellook"
	var b2 bool = true
	b2 , _ = strconv.ParseBool(str5)
	fmt.Printf("b2 type %T b2=%v\n",b2, b2)
	// 输出信息:b2 type bool b2=false
}

3.1.6.2 基本数据类型转string

// 在开发中,经常需要将基本数据类型转成string类型
// 或者将string类型转成基本数据类型
package main

import (
	"fmt"
	_ "unsafe"
	"strconv"
)

func main()  {

	var num1 int = 99
	var num2 float64 = 22.222
	var b bool = true
	var mychar byte = 'h'
	var str string	//空的str

	// 使用第一种方式来转换 fmt.Sprintf方法

	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%c", mychar)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 第二种方式转换 strconv 函数

	var num3 int = 99
	var num4 float64 = 23.456
	var b2 bool = true

	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 'f'格式;10表示小数部分保留几位;64表示这个小数是float64
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)
	// 输出:str type string str="23.4560000000"

	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T str=%q\n", str, str)

	// 第三种 strconv包中有一个函数Itoa,可以直接把一个int转为string
	var num5 int = 4567
	str = strconv.Itoa(num5)
	fmt.Printf("str type %T str=%q\n", str, str)
}

3.1.6.3 基本数据类型转换

package main
import (
	"fmt"
	_ "unsafe"  // 如果我们没有使用到一个包,但是不想去掉,就在前边加一个 _ 表示忽略
)

func main()  {
	var i int = 100

	// 将i ==> float

	var n1 float32 = float32(i)
	var n2 int8 = int8(i)
	var n3 int64 = int64(i)

	fmt.Printf("i=%v n1=%v n2=%v n3=%v\n", i, n1, n2, n3)

	// Go中数据类型的转换可以是从表示范围小-->表示范围大 也可以范围大-->范围小
	// 被转换的是变量存储的数据(即值),变量本身的数据类型并没有发生变化

	// 在转换中,比如讲int64 转换成 int8 [-128~127] ,编译时不会报错
	// 只是转换的结果是按照溢出处理的,和我们希望的结果不一样
	var num1 int64 = 999999
	var num2 int8 = int8(num1)
	fmt.Println("num2=", num2)
	// 输出结果:num2= 63

	var (
		m1 int32 = 12
		m2 int64
		m3 int8
	)
	m2 = int64(m1) + 20
	m3 = int8(m1) + 20

	fmt.Println("m2=", m2, "m3=", m3)

	var (
		b1 int32 = 12
		b2 int8
		// b3 int8
	)
	b2 = int8(b1) + 127		// 编译通过,但是结果不是127+12,按照溢出处理,输出结果为·117
	// b3 = int8(b1) + 128  编译不通过

	fmt.Println(b2)
}

3.1.7 Scanln && Scanf

3.1.7.1 Scanf

package main

import (
	"fmt"
)

// 可以从控制台接收用户信息(姓名、年龄、薪水、是否通过测试)
func main()  {
	var name string
	var age byte
	var sal float32
	var isPass bool

	// scanf可以按照指定的格式输入
	fmt.Println("姓名、年龄、薪水、是否通过测试,输入时请使用空格隔开")
	fmt.Scanf("%s %d %f %t", &name, &age, &sal, &isPass)
	fmt.Printf(" 名字是%v \n 年龄是 %v \n 薪水是%v \n 是否通过考试 %v \n", name, age, sal, isPass)
}

3.1.7.2 Scanln

package main

import (
	"fmt"
)

// 可以从控制台接收用户信息(姓名、年龄、薪水、是否通过测试)
func main()  {
	// 先声明变量
	var name string
	var age byte
	var sal float32
	var isPass bool
	
	fmt.Println("请输入姓名:")
	fmt.Scanln(&name)
	
	fmt.Println("请输入年龄:")
	fmt.Scanln(&age)
	
	fmt.Println("请输入薪水:")
	fmt.Scanln(&sal)

	fmt.Println("请输入是否通过考试:")
	fmt.Scanln(&isPass)
	
	fmt.Printf(" 名字是%v \n 年龄是 %v \n 薪水是%v \n 是否通过考试 %v \n", name, age, sal, isPass)
}

3.2 Escaper Char(转义字符)

名称 含义
\t 一个制表位,实现对齐的功能
\n 换行符
\ 一个 \
" 一个 "
\r 一个回车,从当前行的最前端开始输出,覆盖之前的内容
// 例子
package main

import "fmt"

func main() {
	fmt.Println("tom\tJerry")
	fmt.Println("tom\nJerry")
	fmt.Println("D:\\goproject\\src\\go_code\\project01\\main")
	fmt.Println("你好我是RSQ\r哈哈")
}

3.3 Operator(运算符)

3.3.1 基本运算符

package main

import (
	"fmt"
)

func main()  {
	// / %
	// 运算的数都是整数,那么除后,去掉小数部分,保留整数部分
	fmt.Println(10 / 3)	   // 输出3

	var a1 float32 = 10 / 4
	fmt.Println(a1)	// 输出2

	// 最后所取数取决于参与运算的数值类型
	// 若希望保留小数部分,则需要有浮点数参与运算
	var a2 float32 = 10.0 / 4
	fmt.Println(a2)  // 输出2.5


	// 取模运算%
	// 有个公式	a % b = a - a / b * b
	fmt.Println("10%3=", 10 % 3)	// = -1
	fmt.Println("-10%3=", -10 % 3)  // = -10 - (-10) / 3 * 3 = -10 - (-9) = -1
	fmt.Println("10%-3=", 10 % -3)	// = 1
	fmt.Println("-10%-3=", -10 % -3)	// = (-10) - (-10) / (-3) * (-3) = -10 - (-9) = -1


	// ++ -- 的使用
	var i int = 10
	i++
	fmt.Println("i=", i)
	i--
	fmt.Println("i=", i)

	// 关系运算符,其结果都是bool型,也就是要么是true,要么是false
	// 关系表达式经常用在if结构的条件中,或循环结构的条件中
	// == != < > <= >=
	// 比较运算符==不能写成一个 = 号,一个代表赋值

	var n1 int = 9
	var n2 int = 8
	fmt.Println(n1 == n2)	// false
	fmt.Println(n1 != n2)	// true
	fmt.Println(n1 > n2)	// true
	fmt.Println(n1 < n2)	// false
	fmt.Println(n1 >= n2)	// true
	fmt.Println(n1 <= n2)	// false
	flag := n1 > n2
	fmt.Println("flag=", flag)	// true

	// 逻辑运算符
	// 用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个bool值。
	var age int = 40
	if age > 30 && age < 50 {
		fmt.Println("ok1")
	}

	if age > 30 || age < 40 {
		fmt.Println("ok2")
	}

	if age < 40 {
		fmt.Println("ok3")
	} else if age >= 40 {
		fmt.Println("ok4")
	}

	if !(age > 30) {
		fmt.Println("ok5")
	}

	var m int = 10
	// 调用一下test函数,短路与
	if m > 9 && test() {
		fmt.Println("okoko")
	}


	// 交换两个变量的值
	// 法一:
	m1 := 10
	m2 := 5
	t := m1
	m1 = m2
	m2 = t
	fmt.Println("m1和m2交换后值为:", m1, m2)
	
	// 法二,可以不使用中间变量:
	m1, m2 = m2, m1
	fmt.Println("m1和m2交换后值为:", m1, m2)

	// 法三:但是这种会有溢出的风险
	// a = a + b
	// b = a - b
	// a = a - b 
	
	// 赋值运算符的左边 只能是变量,右边可以是变量、表达式、常量值
	// 表达式:任何有值的都可以看做表达式
	var d int 
	d = m1
	d = 8 + 2 * 8
	d = test1() + 90
	d = 890
	fmt.Println(d)


}

func test1() int{
	return 90
}

func test() bool {
	fmt.Println("test......")
	return true
}

3.3.2 其它运算符

package main

import (
	"fmt"
)

// & * 运算符
func main()  {
	a := 100
	fmt.Println("a 的地址=", &a)

	var ptr *int = &a
	fmt.Println("Ptr 指向的值是=", *ptr)


	// 求三个数中的最大值
	var b int = 10
	var c int = 50
	var d int = 30
	var max int
	if b > c && b > d {
		max = b
	} else if c > b && c > d {
		max = c
	} else {
		max = d
	}
	fmt.Println("最大值max=", max)
}

// go语言中没有三元运算符,如果要实现可以用if else来实现
// 传统三元  a = b > c ? b : c

3.3.3 shift(移位运算)

对于有符号的数而言:
1)二进制最高位是符号位,0表示正数,1代表负数
    1 ====》「0000 0001」  -1 ====》「1000 0001」
2)正数的原码、反码和补码都一样
3)负数的反码=它的原码,符号位不变,其他位取反(0->1,1->0)
    1 ====》反码:「0000 0001」 补码:「0000 0001」
   -1 ====》反码:「1000 0001」 补码:「1111 1111」
4)负数的补码等于它的反码+1
5)0的反码和补码都是0
6)在计算机运算的时候,都是以补码的方式来运算的

Golang中有三个位运算
    1)&:与运算:两位全为1,结果为1,否则为0
    2)|:或运算:两位有一个为1,结果为1,否则为0
    3)^:异或运算:两个一个为0,一个为1,结果为1,否则为0

    2&3  0000 0010 & 
         0000 0011      ==》0000 0010 =》 2

    2|3  0000 0011  = 》3
    2^3  0000 0001 = 》1

Golang移位运算

>>右移位运算符:低位溢出,符号位不变,并用符号位补溢出的高位
<<左移位运算符:符号位不变,低位补0


1>>2 0000 0001 ==> 0000 0000 ===>0
1<<2  0000 0001 ==> 0000 0100 ===>4

示例:

package main

import (
 "fmt"
)

func main() {
 var a int = 1 >> 2
 var b int = -1 >> 2
 var c int = 1 << 2
 var d int = -1 << 2
 
 fmt.Println("a=", a)  // a= 0
 fmt.Println("b=", b)  // b= -1
 fmt.Println("c=", c)  // c= 4
 fmt.Println("d=", d)  // d= -4

 fmt.Println(2&3)		// 2
 fmt.Println(2|3)		// 3
 fmt.Println(-2^2)		// -4
 fmt.Println(2^3)		// 1
 fmt.Println(13&7)		// 5
 fmt.Println(5|4)		// 5
 fmt.Println(-3^3)		// -2

}

3.3.4 example

package main

import (
	"fmt"
)

func main()  {
	// (1)假如还有97天放假,问还有xx个星期零xx天
	days := 97
	week := days / 7
	leftdays := days % 7
	fmt.Printf("还有%d个星期零%d天 \n", week, leftdays)

	// (2)定义一个变量保存华氏温度,华氏温度转换摄氏温度的公式如下:
	// 5/9*(华氏温度-100),请求出华氏温度对应的摄氏温度
	var huashi float32 = 134.2
	// 下边的5一定要写成5.0,不然5/9=0
	var sheshi float32 = 5.0 / 9 *  (huashi - 100)
	fmt.Printf("%v 对应的摄氏温度为 %v", huashi, sheshi)
}

3.3.5 attention

操作符使用注意事项:
(1)Golang的自增自减只能当做一个独立的语言使用,不能这样使用: b := a++ 或者 b := a--
(2)Golang的++和--只能写在变量的后边,不能卸载变量的前面,即:只有a++ a--而没有 ++a --a
(3)Golang的设计者去掉c/java中的自增自减的容易混淆的写法,让Golang更加简洁,统一(强制性的)

 逻辑运算符一览:
 假定A为True,B为False

 &&     逻辑与运算符。如果两边都是True则其才为True,否则为False。         (A && B)为False
 ||     逻辑或运算符。如果两边有一个为True,则其位True,否则为False       (A || B)为True
 !      逻辑非运算符。如果条件为True,则逻辑为False,否则为True           (A && B)为True

&&也叫短路与:如果第一个条件为false,则第二个条件不会判断,最终结果为false
||也叫短路或:如果第一个条件为true,则第二个条件不会判断,最终结果为true

赋值运算符:

=、+=、-=、*=、/=、%=

位赋值运算符(跟二进制相关)
<<==  左移后赋值        C <<== 2 等于 C = C << 2
>>==  右移后赋值
&=    按位与后赋值
^=    按位异或后赋值
|=    按位或后赋值

运算符优先级:
1、括号,++,--
2、单目运算符   + - ! ~ 
3、算术运算符
4、移位运算符
5、关系运算符
6、位运算符
7、逻辑运算符
8、赋值运算符
9、逗号

只有单目运算符、赋值运算符是从右向左运算的

3.4 System(进制)

转换原理:

八进制转10进制:从最低位开始(右边的),将每个位上的数提取出来,乘以8的(为数-1)次方,然后求和。
0123 = 3 * 1 + 2 * 8 + 1 * 8 * 8 = 3 + 16 + 64 = 83

16进制转换10进制:从最低位开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和。
0x34A = 10 * 1 + 4 * 16 + 3 * 16 * 16 = 10 + 64 + 768 = 842 (A=10,B=11,C=12,D=13,E=14,F=15)

110001100 = 2^2 + 2^3 + 2^7 + 2^8 = 4 + 8 + 128 + 256 = 396

02456 = 6 * 1 + 5 * 8 + 4 * 8 * 8 + 2 * 8 * 8 * 8 = 1326

0xA45 = 5 * 1 + 4 * 16 + 10 * 16 * 16 = 2629

进制规则:将该数不断除以2/8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的2/8进制数

二进制转八进制:将二进制数每三位一组(从低位开始组合),转成对应的八进制数即可。
11010101 = 11 010 101 = 03 2 5 = 0325

二进制转16进制:将二进制数四位一组(从低位开始组合),转成对应的十六进制数即可。
11010101 = 1101 0101 = 0x D 5 = 0xD5

八进制转二进制:跟上述反之即可,将八进制每1位,转成对应的一个3位的二进制数即可

16进制转二进制:将16进制每1位,转成对应的一个4位的二进制数即可。

例子:

package main

import (
	"fmt"
)

func main()  {
	var i int = 5

	// 打印二进制%b
	fmt.Printf("%b\n", i)
	
	// 八进制
	var j int = 011
	fmt.Println("j=", j)	// 打印出9

	// 十六进制
	var k int = 0x11
	fmt.Println("k=", k)
}

3.5 Pointer(指针)

3.5.1 example

package main

import (
	"fmt"
)

func main()  {
	var i int = 100
	fmt.Printf("i的内存空间地址=%v\n", &i)

	// ptr是一个指针变量
	// ptr指针指向的值 *int
	// ptr本身的地址 &ptr
	var ptr *int = &i
	fmt.Println("ptr指针变量的值=",ptr)
	fmt.Printf("ptr指针变量内存地址=%v\n", &ptr)
	fmt.Printf("ptr指针变量指向的地址的值=%v\n", *ptr)

	// 讲num的值赋值给ptr,并通过ptr去修改num的值
	var num int = 9
	fmt.Printf("num address %v\n", &num)

	var ptr1 *int
	ptr1 = &num
	*ptr1 = 10
	fmt.Println("num=",num )
	
	// 练习:以下代码输出什么内容
	var a int = 300
	var b int = 400
	var ptr2 *int = &a
	*ptr2 = 100
	ptr2 = &b
	*ptr2 = 200
	fmt.Printf("a=%d,b=%d,*ptr2=%d",a ,b ,*ptr2)
	// 输出内容:a=100,b=200,*ptr2=200
}

3.5.2 attention

(1)基本数据类型,变量存储的就是值,也叫值类型
(2)获取变量的地址,用&,比如:var num int 获取num的地址:&num
(3)指针类型,变量存的是一个地址,这个地址指向的空间存的才是值,比如:var ptr *int = &num
(4)获取指针类型所指向的值,使用:*,比如:var ptr *int,使用*ptr获取ptr指向的值
(5)值类型,都有对应的指针类型,形式为 *数据类型,比如int的对应指针就是  *int, 
	float32对应的指针类型就是 *float32。以此类推
(6)值类型包括:基本数据类型 int系列,float系列,bool,string、数组和结构体struct
        值类型变量直接存储时,内存通常在栈中分配(golang中有逃逸分析,所以有可能值类型还会在堆中)
        GoLang编辑器会根据变量使用多长时间来决定在哪,比如一个变量生存时间比较长,那可能就放在堆区
        还比如有一个值类型在其他函数中可能也会用到,那么这个值类型就有可能会在堆区存放
(7)引用类型:指针、slice切片、map、管道chan、interface等都是引用类型
        引用类型变量存储的是一个内存地址,这个地址对应的空间才是真正存储数据(值),
        内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,有GC来回收。

3.6 Controlling(控制语句)

3.6.1 if

if的条件语句不能是赋值语句

package main

import (
	"fmt"
)

func main()  {
	//	declare variable
	var age byte

	fmt.Println("请输入年龄:")
	fmt.Scanln(&age)

	// 变量作用域只在本分支循环内
	if age > 18 {
		fmt.Println("应该付法律责任了")
	} else {
		fmt.Println("You will die.")
	}

	if age := 20; age > 18 {
		fmt.Println("应该付法律责任了")
	}

	
	// 嵌套分支不宜过多,最多控制在三层
	var month byte
	fmt.Println("请输入月份:")
	fmt.Scanln(&month)
	fmt.Println("请输入年龄:")
	fmt.Scanln(&age)
	if month >= 4 && month <= 10 {
		// 旺季
		if age >= 18 && age < 60 {
			fmt.Printf("%v 月旺季,%v 岁成人票价是60", month, age)
		} else if age < 18 {
			fmt.Printf("%v 月旺季,%v 岁儿童半价", month, age)
		} else {
			fmt.Printf("%v 月旺季,%v 岁老人票价1/3", month, age)
		}
	} else if month >= 1 && month < 4 || month > 10 && month <= 12 {
		// 淡季
		fmt.Println("淡季成人票价:40")
		fmt.Println("淡季儿童票价:20")
	} else {
		fmt.Println("错误的输入信息,请重新输入")
	}
}

示例:

package main

import (
	"fmt"
	"math"
)

func main()  {
	// 1、声明两个int32变量并赋值,判断两者之和是否大于50,是的话输出hello,world
	var a1 int32 = 31
	var b11 int32 = 30
	
	sum1 := a1 + b11

	if sum1 > 50 {
		fmt.Println("hello,world")
	}

	// 2、声明2个float32型变量并赋值,判断第一个数是否大于10,第二个数是否小于20,打印两者之和
	var a11 float32 = 11.0
	var a22 float32 = 12.0

	if a11 > 10 && a22 < 20{
			fmt.Println("sum=", (a11 + a22))
	}
	
	// 3、定义两个int32变量,判断二者之和能否被3又被5整除,并打印提示信息
	var b1 int32 = 5
	var b2 int32 = 11
	sum3 := b1 + b2
	// t1 = sum3 / 3
	// t2 = sum3 / 5

	if sum3 % 3 == 0 && sum3 % 5 == 0 {
		fmt.Println("符合要求")
	} else {
		fmt.Println("不符合要求")
	}

	// 4、判断一个年份是否是闰年,闰年的条件是符合下面两者之一:
	// (1)年份能被4整除,但是不能被100整除
	// (2)能被400整除
	var year int32 = 2000
	if year %4 == 0 && year % 100 != 0 {
		fmt.Printf("%d是闰年\n", year)
	} else if year % 400 == 0 {
		fmt.Printf("%d是闰年\n", year)
	} else {
		fmt.Printf("%d不是闰年\n", year)
	}

	// 求ax^2 + bx + c = 0 方程的根;如果b^2- 4ac > 0  则有两个解;b^2- 4ac = 0,则有一个解;b^2- 4ac < 0 无解
	// 引入一个包math,里边有很多函数,比喻可以求平均值、绝对值等等
	var a float64 = 2.0
	var b float64= 4.0
	var c float64 = 2.0
	m := b * b - 4 * a * c
	
	if m > 0 {
		x1 := (-b + math.Sqrt(m)) / (2 * a)
		x2 := (-b - math.Sqrt(m)) / (2 * a)
		fmt.Printf("x1=%v x2=%v", x1, x2)
	} else if m == 0 {
		x1 := (-b - math.Sqrt(m)) / (2 * a)
		fmt.Printf("x1=%v", x1)
	} else {
		fmt.Printf("无解")
	}

}

3.6.2 for

3.6.2.1 exa1

package main

import (
	"fmt"
)

func main()  {

	// 1、打印1~100之间所有是9的倍数的整数的个数及总和
	var max int = 100
	var count int = 0
	var sum int = 0
	for i := 1; i <= max; i++ {
		if i % 9 == 0 {
			count++
			sum += i
		}
	}

	fmt.Printf("count=%v sum=%v\n", count, sum)

	// 2、打印以下内容
	// 0 + 6 = 6
	// 1 + 5 = 6
	// 2 + 4 = 6
	// 3 + 3 = 6
	// 4 + 2 = 6
	// 5 + 1 = 6
	// 6 + 0 = 6
	// 法一:
	for i := 0; i <= 6; i++ {
		for j :=6; j >= 0; j-- {
			if i + j == 6 {
				fmt.Printf("%d + %d = %d\n", i, j, (i + j))
			}
		}
	}

	fmt.Printf("\n")

	// 法二:
	var n int = 60
	for i := 0; i <= n; i++ {
		fmt.Printf("%v + %v = %v \n", i, n - i, n)
	}
}

3.6.2.2 exa2

package main

import (
	"fmt"
)

// 统计3个班成绩情况,每个班有5名同学
// 1、求出各个班的平均分和所有班级的平均分[学生的成绩从键盘输入]
// 2、求出一共有多少人及格
func main()  {
	var classNum int = 3
	var stuNum int = 5
	var passCount int = 0
	var ave float64 = 0.0
	// var ave float64 = 0.0
	for j := 1; j <=classNum; j++ {
		var sum float64 = 0.0
		for i := 1; i <= stuNum; i++ {
			var score float64
			fmt.Printf("请输入第%d班的%d个学生的成绩\n", j, i)
			fmt.Scanln(&score)
			// 累计总分
			sum += score
			// 判断分数是否及格
			if score >= 60 {
				passCount++
			}
		}
		fmt.Printf("第%d个班级的平均分是%v\n", j, sum / float64(stuNum))
		// 将各个班的总成绩累积到ave变量
		ave += sum
	}

	fmt.Printf("所有班级的总成绩是%v 所有班级的平均分%v\n", ave, ave / (float64(stuNum * classNum)))
	fmt.Printf("及格人数有%v名学生\n", passCount)

}

3.6.2.3 exa3

package main

import (
	"fmt"
)

func main()  {

	// 打印半个金字塔
	/*
	*
	**
	***
	****
	*****
	******
	*/
	for i :=1; i <= 6; i++ {
		for j := 1; j <= i; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}


	// 打印整个金字塔
	// 思路:每行*的数量有个规律 即 2 * 层数 - 1
	// 每一行前的空格数量等于总的层数-当前层数
	/*
         *
        ***
       *****
      *******
     *********
    ***********
	*/
	var floor int = 7
	// i表示层数
	for i :=1; i <= floor; i++ {
		// k表示在每一行前打印多少空格
		for k := 1; k <=floor - i; k++ {
			fmt.Print(" ")
		}
		// j表示每一行打印多少*
		for j := 1; j <= 2 * i - 1; j++ {
				fmt.Print("*")
			}
		fmt.Println()
	}

		fmt.Println()
	
	// 打印空心金字塔
	/*
	 *
	* *
   *   *
  *     *
 *********
 */
	var floor1 int = 7
	// i表示层数
	for i :=1; i <= floor1; i++ {
		// k表示在每一行前打印多少空格
		for k := 1; k <=floor1 - i; k++ {
			fmt.Print(" ")
		}
		// j表示每一行打印多少*
		for j := 1; j <= 2 * i - 1; j++ {
			if j == 1 || j == 2 * i - 1 || i == floor1{
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Println()
	}
	fmt.Println()

	// 打印实心菱形
	var n int = 7
	// 打印上半部分
	for i := 1; i <= n; i++ {
		for k := 1; k <= n-i; k++ {
			fmt.Print(" ")
		}

		for j := 1; j <= 2 * i-1; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
	// 打印下半部分
	for i := n - 1; i >= 1; i-- {
		for k := 1; k <= n-i; k++ {
			fmt.Print(" ")
		}

		for j := 1; j <= 2 * i-1; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
	
	// 打印空心菱形
	// 打印上半部分
	var n1 int = 7
	for i :=1; i <= n1; i++ {
		// k表示在每一行前打印多少空格
		for k := 1; k <=n1 - i; k++ {
			fmt.Print(" ")
		}
		// j表示每一行打印多少*
		for j := 1; j <= 2 * i - 1; j++ {
			if j == 1 || j == 2 * i - 1 {
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Println()
	}

	// 打印下半部分
	for i :=n1 - 1; i >= 1; i-- {
		// k表示在每一行前打印多少空格
		for k := 1; k <=n1 - i; k++ {
			fmt.Print(" ")
		}
		// j表示每一行打印多少*
		for j := 1; j <= 2 * i - 1; j++ {
			if j == 1 || j == 2 * i - 1 {
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Println()
	}
}

3.6.2.4 exa4

package main

import (
	"fmt"
)

func main()  {

	// 打印 9 * 9 乘法口诀
	
	for i := 1; i < 10; i++ {
		for j :=1; j <= i; j++ {
			fmt.Printf("%v * %v = %v\t", j, i, (i * j))
		}
		fmt.Println()
	}
	
}

3.6.2.5 exa5

package main

import (
	"fmt"
)

func main()  {

	// 判断一个数是不是水仙花数
	// 水仙花数是指一个三位数,其各个位上数字立方和等于其本身。
	// 如:153 = 1*1*1 + 3*3*3 + 5*5*5

	for i := 100; i <= 999; i++ {
		var j = i / 100
		var k = i /10 % 10
		var l = i % 10
		if i == j * j * j + k * k * k + l * l * l {
			fmt.Println(i)
		}
	}
	
}

3.6.2.6 exa6

package main

import (
	"fmt"
)

// 根据输入的月份和年份,求出该月的天数(1-12月)
// 1、3、5、7、8、10、12	==>	31天
// 2	==>	闰年29天,平年28天
// 其它	==> 30天
func main()  {
	var year int
	var month int

	fmt.Println("请输入一个月份")
	fmt.Scanln(&month)

	fmt.Println("请输入一个年份")
	fmt.Scanln(&year)

	switch {
		case month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12:
			run(year)
			fmt.Printf("%v月31天\n", month)

		case month == 2:
			if run(year) == 1 {
				fmt.Printf("%v月是29天", month)
			} else if run(year) == 2 {
				fmt.Printf("%v月是28天", month)
			}

		case month == 4 || month == 6 || month == 9 || month == 11:
			run(year)
			fmt.Printf("%v月30天\n", month)

		default:
			fmt.Println("输入错误")
	}
}

func run(b int) int {
	if b % 4 == 0 && b % 100 != 0 || b % 400 == 0 {
		fmt.Printf("%v年是润年", b)
		return 1
	} else {
		fmt.Printf("%v年是平年", b)
		return 2
	}
}


3.6.2.7 main

package main

import (
	"fmt"
)

func main()  {
	// for循环,打印10句Hello,World
	// for i := 0; i <= 10; i++ {
	// 	fmt.Println("Hello,World")
	// }
	
	// for循环第二种用法
	k := 1
	for k <= 10 {
		fmt.Println("Hello,World")
		k++
	}

	// for循环第三种用法
	// 等价于for ; ; {} 是一个无限循环,通常需要配合break语句来使用
	j := 1
	for {
		if j <= 10 {
			fmt.Println("Hello,World")
			j++
		} else {
			break
		}
	}

	// for-range方式来遍历字符串和数组
	// 法一:传统方式
	var str string = "hello,world"
	for i := 0; i < len(str); i++ {
		fmt.Printf("%c\n", str[i])
	}

	// 法二:for-range
	// 用这种方法不会出现乱码,因为其是按照字符来遍历的
	for index, val := range str {
		fmt.Printf("index=%d value=%c \n", index, val)
	}
	// 如果字符串含有中文,那么传统的遍历字符串的方式就是错误的,会出现显示乱码
	// 原因是因为传统对字符串的遍历是按照字节来遍历的,而一个汉字在UTF-8编码是对应三个字节,所以会报错
	// 解决办法:需要将str转成[]rune切片

	var str1 string = "hello,world北京"
	str2 := []rune(str1)	//就是把str转成[]rune切片
	for i := 0; i < len(str2); i++ {
		fmt.Printf("%c\n", str2[i])
	}


}

3.6.2.8 attention

  • 1)多重循环,又称之为嵌套循环
  • 2)一般使用两层,最多不超过3层
  • 3)设外层循环次数m次,内层为n次,则内层循环体实际上需要执行m*n=mn次

3.6.3 while

// Golang中没有while和do...while的语法
// 可以通过for循环来实现
for {
    if 表达式 {
        //循环语句
    }
}

// do while用for实现
// 循环变量初始化
for {
    循环操作语句
    循环变量迭代
    if 循环条件表达式 {
        break //跳出循环
    }
}

var j int = 1 
for {
    fmt.Println("hellmno",j)
    j++
    if j > 10 {
        break
    }
}

3.6.4 continue

3.6.4.1 exa1

package main

import (
	"fmt"
)

func main()  {

	for i := 1; i <= 100; i++ {
		if i % 2 == 0 {
			continue
		}
		fmt.Println("i=", i)

	}

	var positiveCount int
	var negativeCount int
	var num int
	
	for {
		fmt.Println("请输入一个整数:")
		fmt.Scanln(&num)

		if num == 0 {
			break
		} else if num > 0 {
			positiveCount++
			continue
		}
			negativeCount++
		
	}
	fmt.Printf("正数的个数是:%v \n负数的个数是:%v", positiveCount, negativeCount)


}

3.6.4.2 exa2

package main

import (
	"fmt"
)

func main()  {
	// 某人有100000元
	// 当现金>50000,每次交5%
	// 当现金<=50000,每次交1000
	// 计算该人可以经过多少次路口
	var crash float64 = 100000
	var count int = 0
	// var sum int
	for {
		if crash > 50000 {
			crash = crash - crash * 0.05
			count++
			continue
		} else if crash > 0 && crash <= 50000 {
			crash = (crash - 1000)
			count++
		} else if crash < 0 {
			break
		}
	}

	fmt.Printf("该人一共可以经过%v次路口", count)

}

3.6.4.3 main

package main

import (
	"fmt"
)

func main()  {

	for i := 0; i < 4; i++ {
		for j := 0; j < 10; j++{
			if j == 2 {
				continue
			}
			fmt.Println("j=",j)
		}
	}
}

3.6.4.4 attention

continue语句是结束本次循环,如果循环中的后续还有代码将不会再执行,直接执行下一次循环。

continue语句出现在多层嵌套的循环语句中,可以通过标签指明要跳过的是哪一层循环,这个和前面的break标签的使用规则是一样的

3.6.5 break

3.6.5.1 main

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main()  {

	// 生成一个随机数,还需要设置rand种子
	// time.Now().Unix():返回一个从1970:01:01的0时0分0秒到现在的秒数
	rand.Seed(time.Now().Unix())

	// 随机生成1-100的整数(由于上边的rand一直在变化,所以下边也会一直变化)
	n := rand.Intn(100) + 1  // 默认[0 100) 不包括100,所以需要 + 1
	fmt.Println(n)

	// 随机生成一个数,看生成这个数一共用了几次
	var count int = 0
	for {
		n := rand.Intn(100) + 1
		fmt.Println(n)
		count++
		if (n == 99) {
			break
		}
	}
	fmt.Printf("生成99一共运行了%v次", count)
	
}

3.6.5.2 exa1

package main

import (
	"fmt"
)

func main()  {

	// 求100以内的和
	var sum int = 0
	for i := 0; i <= 100; i++ {
		sum += i
		if sum > 20 {
			fmt.Println("第一次大于20时,数值是", i)
			break
		}
	}
	
	// 用户有三次登录机会
	var name string
	var Pass string
	var loginChance int = 3

	for i := 1; i <= 3; i++{
		fmt.Println("请输入账号:")
		fmt.Scanln(&name)
	
		fmt.Println("请输入密码:")
		fmt.Scanln(&Pass)

		if name == "张无忌" && Pass == "888" {
			fmt.Println("登陆成功")
			break
		} else {
			loginChance--
			if loginChance != 0 {
				fmt.Printf("登录错误,你还有%v次机会", loginChance)
			} else {
				break
			}
		}
	}
	if loginChance == 0 {
		fmt.Println("机会用完")
	}
}

3.6.5.3 label

// break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的哪一层语句块

// 标签基本使用

label1:
for {
    label2:
    for {
        if {
            break label1
            break label2
        }
    }
}

3.6.6 return

return使用在方法或者函数中,表示跳出所在的方法或函数。

一般return是用在函数中

  • 1)如果return是在普通的函数中,则表示跳出该函数,即不再执行函数中return后面的代码,也可以理解终止函数
  • 2)如果return是在main函数,表示终止main函数

3.6.7 goto

3.6.7.1 example

package main

import (
	"fmt"
)

func main()  {
	
	// 演示goto语句
	// goto语句一般会和if语句一块使用

	var n int = 20
	fmt.Println("ok1")
	if n > 20 {
		goto label1
	}

	fmt.Println("ok2")
	fmt.Println("ok3")
	fmt.Println("ok4")

	label1:
	fmt.Println("ok5")
	fmt.Println("ok6")

	// 输出信息
	// ok1
	// ok5
	// ok6

}

3.6.7.2 attention

// 1)go语言的goto语句可以无条件地转移到制定程序中的指定的行
// 2)goto语句通常与条件语句配合使用。可用来实现条件转移,跳出循环体等功能
// 3)在Go程序设计中,一般不主张使用goto语句,以免造成程序流程的混乱,使理解和调试程序产生困难。

基本语法:

goto label
label: statement

你可能感兴趣的:(Golang)