4.2.8 指针
基本介绍:1)基本数据类型,变量存的就是值,也叫值类型。2)获取变量的地址,用&。3)指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值。4)获取指针类型所指向的值,使用:*。
使用细节:值类型都有对应的指针类型,形式为数据类型,比如 int的对应的指针就是int, float32对应的指针类型就是float32,依次类推;
值类型包括int,float,bool string,数组,struct;变量直接存储,内存通常在栈中分配
引用类型包括:指针、slice切片、map、管道chan、interface等,变量存储的是一个地址,这个地址对应的空间才真正存储数据值,内存通常在堆上分配,没有任何变量引用地址是,该地址对应数据空间成为垃圾,由GC(Garbage Collection)来回收。
func Zhizhen() {
var num1 int = 10
//&num1用于获取存放的地址
fmt.Println("num1的地址是", &num1)
//1. ptr是一个指针变量;2. ptr的类型是*int;3. ptr本身的值为&num1
//ptrnum1本身值为num1的地址,&获取指针的值,*获取指针指向的值
var ptrnum1 *int = &num1
fmt.Println("ptrnum1值为:", ptrnum1)
fmt.Println("ptrnum1的地址为:", &ptrnum1)
fmt.Println("ptrnum1指向的值为:", *ptrnum1)
//直接修改num1的值
*ptrnum1 = 20
fmt.Println("ptrnum1指向的值为:", *ptrnum1)
}
4.2.9 标识符的命名规范
对各种变量,方法,函数等命名时使用的字符序列成为标识符(可以自己起名字的地方)
规则:1)由26个字母大小写,0-9,_组成。
2)数字不可开头 1num := 10 //false
3)Golang中严格区分大小写。
4)标识符中不能包含空格
5)不能用系统保留关键字做标识符,比如if,break,while等等,一般不使用预定义标识符,所有如下:
保留关键字:
break | default | func | interfance | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrought | if | range | type |
continue | for | import | return | var |
预定义标识符(包括基本数据类型和系统内嵌函数)
append | bool | byte | cap | close | complex |
---|---|---|---|---|---|
complex64 | complex128 | uint16 | copy | false | float32 |
float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new |
nil | panic | uint64 | println | real | |
recover | string | true | uint | uint8 | uintprt |
6)下划线"_"本身在Go中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但是它对应的值会被忽略(比如:忽略某个返回值)。所以仅能被作为占位符使用,不能作为标识符使用。
func biaoshifu() {
//_用于占位,即返回多个函数值时可以使用
sum2, _ := getSumAndSub(10, 50) //只返回第二个值
fmt.Println("sum2=", sum2)
//可以使用int等数据结构走位变量,但是不推荐
int := 10
fmt.Println(int)
}
注意事项:1)包名:package的名字和目录保持一直,不要使用标准库的名字。
2)变量名,函数名,常量名采用驼峰法xxxYyyyZzzz
3)如果变量名、函数名、常量名首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用(注:可以简单的理解成,首字母大写是公开的,首字母小写是私有的),在golang 没有public , private等关键字。
4. 3运算符
运算符时一种特殊的符号,用以表示数据的运算、赋值和比较等等,主要分为六大类:
1)算术运算符 2)赋值运算符 3)比较运算符 4)逻辑运算符 5)位运算 6)其他运算符
Golang中不存在三元运算符
4.3.1 算数运算符
对数值类型的变量进行运算(加减乘除)
运算符 | 运算 |
---|---|
+ | 正数,加法,字符串相加 |
- | 负数,减法 |
* | 乘法 |
/ | 除法(运算都是整数,除后只保留整数部分10/4=2) |
% | 取模 |
++ | 自增(+1) |
– | 自减 |
注意:
1)除法运算时,整数相除只保留整数部分;需要保留小数部分则需要有浮点数参与运算
2)取模运算本质运算为:a%b = a - a / b * b
3)golang中的++,–只能当作单独语言使用,不能直接赋值a := b++,也不能 i = i ++
4)++,–只能存在与变量后面,不存在++a,–a。(Golang去点c/java中容易混淆的写法)
func yunsuanfu() {
/*除法*/
//整数相除只保留整数部分
fmt.Println(10 / 4) //输出2
//如果需要保留小数部分,则需要有浮点数参与运算
fmt.Println(10.0 / 4) //输出2.5
/*取模*/
//标准公式为a%b = a - a / b * b
fmt.Println(10 % 3)
fmt.Println(-10 % 3) //= -10 - (-10) / 3 * 3 = -10 - (-9) = -1
}
4.3.2 关系运算符
关系运算符的结果都是bool类型,返回值也就是要么是true,要么是false
关系表达式 经常用在if或者循环结构中。
运算符 | 运算 |
---|---|
== | 相等于 |
!= | 不等于 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
注意:1)返回值只能是true或者是false
2)有关系运算符组成的表达式称为关系表达式
3)”==“表示相等,”=“表示赋值,不能混用,需要特别注意
/*关系运算符*/
fmt.Println(9 != 8) //true
fmt.Println(10 <= 7) //false
4.3.4 逻辑运算符
用于连接多个条件(关系表达式),最终结果也是bool
&&:逻辑与,两边都为真则真
||:逻辑或,有一个为真则为真
!:逻辑非,逻辑相反
注意:1)&&短路与运算中,如果第一个条件为false,则第二个条件不再判断,最终结果为false
2)||短路或运算中,如果第二个条件为true,则第二个条件不会判断,最终结果为true。
func test() bool {
fmt.Println("查看是否输出")
return true
}
/*逻辑运算符*/
num2 := 10
if num2 < 9 && test() {
fmt.Println("&&后面语句是否执行") //不输出
}
if num2 > 9 && test() {
fmt.Println("&&test()语句是否执行") //既输出test()又输出结果
}
if num2 > 9 || test() {
fmt.Println("||后面语句是否执行") //只输出该语句
}
4.3.5 赋值运算符
赋值运算符就是将某个运算后的值,付给指定的变量。
运算符 | 描述 | 实例 |
---|---|---|
= | 简单赋值 | c = a + b 将a+b的值赋给c |
+= | 相加后赋值 | c += a :: c = c + a |
-= | 相减后赋值 | c -= a : c = c - a |
*= | 相乘后赋值 | c *= a : c = c * a |
/= | 相除后赋值 | c/-= a : c = c / a |
%= | 取模后赋值 | c %= a : c = c %a |
<<= | 左移后赋值 | c <<= 2 :: c = c << 2 |
>>= | 右移后赋值 | c >>= 2 :: c = c >> 2 |
&= | 按位与后赋值 | c &= 2 :: c = c & 2 |
^= | 按位异或后赋值 | c ^= 2 :: c = c ^ 2 |
!= | 按位或后赋值 | c != 2 :: c = c ! 2 |
注意:1)运算顺序从右往左:先计算,再赋值
2)赋值运算符的左边只能是变量,右边可以是变量,表达式,常量值
func test() int {
return 90
}
/*赋值运算符*/
var d int
d = 8 + 2 * 8
d = test() + 90
d = 890
fmt.Println(d)
不允许使用中间变量交换a和b的值,用a保存a+b,再用a+b-b获得a的值赋给b,再做减法赋给a
//不使用中间变量交换值
a := 10
b := 20
a = a + b
b = a - b
a = a - b
4.3.6 位运算符
计算机进行运算时都是按照二进制补码进行运算的:
运算符 | 描述 |
---|---|
& | 全一为一,否则为0 |
| | 有一为一,否则为0 |
^(异或) | 两者不同(0/1)才能为一,否则为0 |
/*位运算符*/
fmt.Println(2 & 3) //2
fmt.Println(2 | 3) // 3
fmt.Println(2 ^ 3) // 3
fmt.Println(12 & 3) //0
fmt.Println(5 | 4) //5
fmt.Println(-3 ^ 3) //-2
/*2的补码 0000 0010
3的补码 0000 0010
2&3 0000 0010 ==>2*/
4.3.7 其他运算符
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a:给出机器数的实际地址 |
* | 指针变量 | 描述为指针 |
4.3.8运算符优先级
4.4 程序流程控制
程序运行的流程控制,决定程序是如何执行的,主要有三大流程控制语句:1)顺序控制。2)分支控制。3)循环控制
4.4.1 顺序控制
程序从上到下逐行执行,中间没有任何判断和跳转。也是最基本的程序运行方式,按照程序从上至下的阅读顺序进行运行。
需要注意,定义变量时必须采用合法的前向引用,正确调用之前的内容。
4.4.2 分支控制
主要有三种形式:1)单分支。2)双分支。3)多分支
4.4.2.1 单分支
golang支持再if中,直接定义一个变量
语法:/*if 条件表达式{执行代码块}*/
//单分支
var age int
fmt.Scanln(&age)
if age >= 18 {
fmt.Println("已经成年了!")
}
//在条件中直接赋值
if age2 := 20; age2 > 20 {
fmt.Println("已经不是小孩子了")
}
4.4.2.2 双分支
双分支指挥执行其中的一条分支
语法:
/*if 条件表达式{
执行代码块
}else{
代码块
}*/
var a int
var b int
if a > 10 {
fmt.Println("a比10大")
if b < 10 {
fmt.Println("b小于10")
} else {
fmt.Println("b大于10")
}
} else {
fmt.Println("没什么好说的")
}
4.4.3 多分支控制
4.4.3.1 一般多分支
如果条件表达式1为真,直接执行代码1;如果1假2真,执行代码2;以此类推,如果都不成立执行else语句【else不是必须的】。
//多分支控制
if age > 0 && age <= 10 {
fmt.Println("还是小孩子")
} else if age > 10 && age <= 20 {
fmt.Println("年轻人")
} else if age > 20 && age <= 30 {
fmt.Println("年轻人")
} else {
fmt.Println("老年人")
}
//案例
b := true
if b == false {
fmt.Println("1")
} else if b {
fmt.Println("2")
} else if !b {
fmt.Println("3")
} else {
fmt.Println("4")
}//输出2
4.4.3.2 嵌套分支
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支结构称为内层分支,外面的分支结构称为外层分支。
嵌套分支不宜过多,建议控制在3层内。
var a int
var b int
if a > 10 {
fmt.Println("a比10大")
if b < 10 {
fmt.Println("b小于10")
} else {
fmt.Println("b大于10")
}
} else {
fmt.Println("没什么好说的")
}
4.4.4 switch分支控制
switch语句用于基于不同条件执行不同动作,每一个case分支都是唯一的,从上到下逐一测试,知道匹配为止。匹配项后面也不需要加break
基本语法:
switch 表达式{
case 表达式1,表达式2,...:
语句块1
case 表达式3,表达式4,...:
语句块2
//多个case
default:
语句块
}
执行的流程及注意:
1)先执行表达式,得到值,然后和case的表达式进行比较,如果相等就匹配到,然后执行对应case的语句块,然后退出switch控制。
2)如果case里的表达式都没有匹配成功,则执行default的语句块,执行后退出switch控制。
3)golang中case后的表达式可以有多个,使用逗号间隔
4)golang中的case语句块不需要写break,默认会有,即默认情况下执行完case语句块后,就直接退出该switch控制结构
5)case/swich后是一个表达式(常量,变量,有返回值的函数都可)
6)default一句不是必须的,switch后面也可以不带表达式,类似if-else语句。
7)swich穿透-fallthrough,如果在case语句后面增加fallthrough,则会继续执行下一个case。但是指挥默认穿透一层,直接忽略掉下一层的判定条件。
var key byte
fmt.Println("请选择输入一个字母")
fmt.Scanf("%c",&key)
switch key {
case 'a':
fmt.Println("输入的字母是a")
fallthrough
case 'b':
fmt.Println("输入的字母是b")
default:
fmt.Println("输入的字母有问题")
}
/*输入a,输出"输入的字母是a 输入的字母是b"*/
8)typeswich:还可以用于判断某个interface变量中实际指向的变量类型。
switch和if的比较:如果判断的具体数值不多,而且符合整数没浮点数,字符串这些类型,建议使用switch;如果对去接判断以及结果为bool类型的判断,使用if。