go 学习笔记(三)
前言
接触了新的语言go,记录一下学习的笔记方便日后温故知新。
一、进制
1.二进制 0,1 满2进1
2.十进制 0-9 满10进1
3.八进制0-7 满8进1 数字0开头
4.十六进制0-9 A-F满16进1以0x 或者 0X开头 此处的 A-F不区分大小写
在golang中不能直接使用二进制来表示一个整数,他沿用了c的特点,可以用%b输出二进制
func main() {
//二进制
i := 5
fmt.Printf("%b \n", i)
//八进制
j := 011
fmt.Println(j)
//十六进制
k := 0x11
fmt.Println(k)
}
从最低为开始(最右侧),每个位数上的数提取出来乘以进制数的(位数-1)次方,然后求和
二进制转十进制
1011 从右到左四个数分别是 0 1 2 3 四个位
计算
1乘以2的0次幂 +
1乘以2的1次幂 +
0 +
1乘以2的3次幂 = 11
八进制转十进制
0123 转成十进制
计算
3*1 + 2*8的一次方 + 1*8的平方 = 83
十六进制转十进制
0x34a
a 是 10 +
4 * 16 的一次幂 +
3 * 16 的二次幂 = 842
十进制转二进制
将该数不断除以2然后将每步得到的余数倒过来就是对应的二进制
将56转成二进制
65/2 商 28 余数 0
28/2 商 14 余数 0
14/2 商 7 余数 0
7/2 商 3 余数 1
3/2 商 1 余数 1 (商为1)
最后得到 111000 = 56
十进制转八进制及十进制转十六进制方法参考十进制转二进制
二进制转八进制及十六进制
二进制转八进制:将二进制的每三位一组(从低位开始组合),转成对应的八进制即可
举例:
将11010101转成八进制
11 010 101 二进制 101 及 010 及 11 分别等于十进制的 5 , 2 , 3 那么11010101转成八进制为325
二进制转十六进制
将二进制的每四位一组(从低位开始组合),转成对应的十六进制即可
八进制十六进制转成二进制
八进制转二进制:将八进制的每一位转成对应的一个3位的二进制数即可
0237
7:111
7:011
7:010
即:八进制的234转为二进制位10011111
十六进制转二进制:将十六进制的每一位转成对应的一个4位的二进制数即可
二、源码反码补码
1.对于有符号的而言
二进制的最高位是符号位,0表示正数,1表示负数
正数的原码反码补码都一样
负数的反码 = 源码符号位不,其他位取反(1 -> 0 , 0 -> 1)
负数的补码 = 它的反码 + 1
0 的反码,补码,都是0
在计算机运算的时候都是以补码的方式来运算的
2.位运算
&:两位全为1结果为否则为0
|:两位有一个为1结果为1否则为0
^:两位有一个为1,另一个为0结果为1否则为0
计算
2&3:
2的补码: 0000 0010
3的补码: 0000 0011
2&3: 0000 0010 转成 十进制的结果:2
2|3:
0000 0011 : 3
2^3:
0000 0001 : 1
-2^2:
-2的源码:1000 0010
-2的反码:1111 1101
-2的补码:1111 1110
2的补码:0000 0010
-2^2的结果的补码:1111 1100
-2^2的结果的反码:1111 1100 - 1 = 1111 1011
-2^2的结果的源码:1111 1011 => 1000 0100 = -4
二进制加减:
二进制的运算算术运算二进制的加法:0+0=0,0+1=1 ,1+0=1, 1+1=10(向高位进位);
二进制的减法:0-0=0,10-1=1(向高位借位) 1-0=1,1-1=0 (模二加运算或异或运算) ;
二进制的乘法:0 * 0 = 0 0 * 1 = 0,1 * 0 = 0,1 * 1 = 1 二进制的除法:0÷0 = 0,0÷1 = 0,1÷0 = 0 (无意义),1÷1 = 1 ;
逻辑运算二进制的或运算:遇1得1 二进制的与运算:遇0得0 二进制的非运算:各位取反。
三、流程控制
1.顺序
2.分支
//if
// if
// if else
// if else if else ...
// func main() {
// if age := 1; age < 18 {
// fmt.Println("小孩")
// } else {
// fmt.Println("大人")
// }
// }
//switch
//golang中,case不需要加break即唯一匹配
//1.case后面是一个表达式可是常量,变量,有返回值的函数
func main() {
var b byte = 'a'
switch test(b) {
case 'a':
fmt.Println("hello a")
case 'b':
fmt.Println("hello b")
default:
fmt.Println("hello default")
}
}
func test(b byte) byte {
return b + 1
}
//2.switch和case的数据类型要保持一致
func main() {
n1 := 20
n2 := 10
switch n2 {
case 1, add(n2), 22:
fmt.Println("hello a")
case n1:
fmt.Println("hello b")
default:
fmt.Println("hello default")
}
}
func add(i int) int {
return i
}
//2.switch当初if else分支来使用
func main() {
n1 := 20
switch {
case n1 < 10:
fmt.Println("hello")
case n1 == 20:
fmt.Println("hi")
default:
fmt.Println("how are you")
}
}
3.循环
for
func main() {
for i := 0; i < 5; i++ {
fmt.Println("hello", i)
}
}
//遍历字符串注意事项:
func main() {
//写法1
for i := 0; i < 5; i++ {
fmt.Println("hello i", i)
}
//写法2
j := 5
for j <= 10 {
fmt.Println("hello j", j)
j++
}
//写法3
i := 3
for {
if i < 6 {
fmt.Println("hello ~", j)
} else {
break
}
i++
}
//遍历字符串和数组 (遍历字符串和数组,此处只给出两种字符串遍历法,数组后续补充),此处需要注意go中字符串是按照字节拼接而不是字符拼接,所以
//在go使用的utf8中一个汉字占用3个字节下标显示的方法中 经典for则会出现乱码,所以遍历可能还有中文字符的字符串时请使用range方式遍历否则
//会产生错误但是可以将字符串强制转成切片遍历则正常
str := "hello,world!你好,世界"
for i := 0; i < len(str); i++ {
fmt.Printf("str %c\n", str[i])
}
strRune := []rune(str)
for i := 0; i < len(strRune); i++ {
fmt.Printf(" strRune %c\n", strRune[i])
}
for index, val := range str {
fmt.Printf("字符:%c === 下标 %d \n", val, index)
}
}
四、函数
//概念:
// 为完成某一功能的程序指令(语句)的集合,成为函数,在go中分为自定义函数和系统函数
//语法:
func 函数名(形参列表) (返回值类型列表){
//执行语句
return 返回值列表
}
//例如:
func NaN() float64 ... //函数返回一个IEEE 754“这不是一个数字”值
五、包
1.import 是写src下的包所在的文件夹abv/edf,然后用包名.函数名来调用包内的函数
2.一般情况下包名和文件夹名保持一致
3.在引包的时候,路径从GOPATH的src开始,不用带src编译器会从src下开始找\
4.为了让其他包的文件可以访问本包的函数,需要将函数大写
5.utils "abc/def/util" 此时 utils为当前包的别名
6.同一个包(package)不能有重名函数
7.如果需要编译成一个可执行文件需要将包名声明为 main 即 package main,这是语法规范,如果是写一个库,则包名可以自定义
8.在$GOPATH下进行打包 build -o 需要指定到main包
六、库文件
1.在编译.go文件之后,会在pkg目录下生成.a库文件,此文件内容是二进制的可以供别人调用,可以起到对源文件隔离的作用
七、函数调用机制
1.在调用一个函数时候会给该函数分配一个新的空间,编译器会通过自身处理让这个新的空间和其他的栈空间区分开来
2.在每个函数对应的栈中,数据空间是独立的,不会相互混淆
3.当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间
八、递归调用
//求结果
func main() {
//写法1
test(4)
}
func test(i int) {
if i > 2 {
i--
test(i)
}
fmt.Println(i)
}
//结果
2
2
3
---
分析
栈
test栈
因为2!>2所以此时
输出语句 2 输出后栈消失回到下一个栈
test栈
因为3>2所以此时
n = 2
输出语句 2 输出后栈消失回到下一个栈
test栈
因为4>2所以此时
n = 3
输出语句 3 输出后栈消失
main栈
test(4)
九、函数注意事项
//1.基本数据类型和数组默认都是值传递,即进行值拷贝,在函数内修改不会影响到原来的值(java中数组传递的是引用类型,在函数内是可以修改到函数外的数组的)
func main() {
mum := 5
fmt.Println("num = ", mum)
test(mum)
fmt.Println("num = ", mum)
}
func test(i int) {
i = i + 1
}
//结果
num = 5
num = 5
//2.如果希望函数内的变量可以修改函数的内的变量,可以传入变量的地址,函数内以指针的方式操作变量,从效果上看类似引用
func main() {
mum := 5
fmt.Println("num = ", mum)
test(&mum)
fmt.Println("num = ", mum)
}
// &取地址 *取指针指向的值
func test(i *int) {
*i = *i + 1
}
//结果
num = 5
num = 6
//3.在go中,函数也是一种数据类型,可以赋给一个变量,该变量就是一个函数类型的变量了,通过该变量可以对函数进行调用
func main() {
sum := getSum
fmt.Printf("sum的类型是 = %T,getSum的类型是 = %T \n", sum, getSum)
sumRes := sum(10, 20)
fmt.Println("sumRes = ", sumRes)
getSumRes := getSum(10, 20)
fmt.Println("getSumRes = ", getSumRes)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
//结果
sum的类型是 = func(int, int) int,getSum的类型是 = func(int, int) int
sumRes = 30
getSumRes = 30
//4.在go中,函数也可以是形参
func main() {
res := myFunc(getSum, 10, 20)
fmt.Println("res = ", res)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
func myFunc(funVal func(n1 int, n2 int) int, num1 int, num2 int) int {
return funVal(num1, num2)
}
//结果
res = 30
//5.为了简化数据类型定义,go可以自定义数据类型
func main() {
type myInt int //相当于给int起了一个叫做myInt的别名,但myInt和int不是同一个数据类型,是两个不同的数据类型,互相之间需要强转才能使用
var num myInt = 10
fmt.Println("num = ", num)
res := myFunc(getSum, 10, 20)
fmt.Println("res = ", res)
}
func getSum(n1 int, n2 int) int {
return n1 + n2
}
type myFuncType func(int, int) int //自定义方法类型
func myFunc(funVal myFuncType, num1 int, num2 int) int {
return funVal(num1, num2)
}
//结果
num = 10
res = 30
//6.go支持对返回值命名
func main() {
sumN, subN := getSum(10, 20)
fmt.Println("sumN = ", sumN, "subN = ", subN)
}
func getSum(n1 int, n2 int) (sum int, sub int) {
sum = n1 + n2
sub = n1 - n2
return
}
//结果
umN = 30 subN = -10
//7.使用_标识符,忽略返回值
//8.支持0-多个可变参数
func main() {
sumN := getSum2(10, 20, 30)
fmt.Println("sumN = ", sumN)
}
//案例:1到多个可变参数的求和
func getSum2(arg1 int, args ...int) (sum int) {
sum = arg1
//args是一个切片
for i := range args {
sum += args[i]
}
return
}
//结果
sumN = 60
十、init函数
1.每一个源文件都可以包含一个init函数,该函数会在main函数之前,被Go运行框架调用
func main() {
fmt.Println("hello main")
}
func init() {
fmt.Println("hello init")
}
//结果
hello init
hello main
2.如果一个文件同时包含全局变量定义,main,init则执行流程是 全局变量定义
//-> init -> main
var age = test()
func test() int {
fmt.Println("hello test")
return 18
}
func main() {
fmt.Println("hello main and here's age is ", age)
}
func init() {
fmt.Println("hello init")
}
//结果
hello test
hello init
hello main and here's age is 18
3.init函数主要是完成一些初始化工作
4.导包:当main包函数使用了别的包,那么首先执行被引入的变量定义,然后是被引入的包的init函数然后才是main包的变量定义,init函数最后是main函数