1、注释
一个项目,在写的时候,你能看懂,半年,一年,时间久了,你就可能忘了。
注释,给自己看,也给别人看,良好习惯
-
单行注释
// 我是单行注释 // 注释主要针对
-
多行注释
/* 我是多行注释 我是多行注释 */ package main import "fmt" /* main函数,go语言的启动入口 */ func main(){ // 打印一句话,并换行 fmt.Println("hello world") }
2、变量
电脑实际识别的是机器码:011101
我们理解的世界,和计算机的世界要一一对应
计算机也需要定义一些东西。
人名:张三,李四
变量的定义
package main
import "fmt"
/*
main函数,go言的启动入口
*/
func main(){
// name 是变量
var name string = "kuangshen"
var age int = 19
name = "zhangsan"
fmt.Println(name,age)
}
变量的定义var关键字
Go语言是静态类型语言,所有的类型我们都要明确的去定义,string表示字符串类型,go语言声明一个变量使用var
关键字
var name type
- name是变量名字
- type是变量类型
同时定义多个变量,变量命名规则遵循驼峰命名,如 userFiles
// 定义变量
var {
name string // 默认值空字符串
age int // 默认值 0
add string
}
var a,b int
不显示赋值,变量使用默认值
- string默认空字符串
- 整形和浮点型变量默认0和0.0
- 布尔型默认false
- 切片、函数、指针变量的默认值为 nil
变量的初始化
标准格式:
var 变量名 类型 = 值 (表达式) // 赋值
比如
var name string = "kuangshen"
var age int = 18
// 变量是可以重复赋值的
age = 20
// 格式化输出
fmt.Printf("name:%s,age:%d",name,age)
只定义变量,不使用在go语言是不行的,无意义的代码就是垃圾,会报错
短变量赋值
像java,go也有语法糖(目的偷懒),自动推到变量类型
func main() {
// := 编译器根据变量的值自动推导变量类型,这种写法叫短变量声明并初始化
// 短变量赋值只能用在局部变量中
name := "kuangshen"
age := 18
fmt.Printf("name:%s,age:%d",name,age)
fmt.Printf("%T,%T",name,age) // %T输出变量的类型
}
执行结果:
小结:
- 定义变量使用定式,var 变量名称
- 给变量赋值,使用变量名赋值
打印内存地址
package main
import "fmt"
func main() {
var num int
num = 100
fmt.Printf("num:%d,内存地址:%p", num, &num) // 取地址符号 &变量名
// 重新赋值,发现内存地址不变,说明变量名代表着一个内存地址,是编译器分配的
num = 200
// 格式化输出
fmt.Printf("num:%d,内存地址:%p", num, &num) // 取地址符号 &变量名
}
变量交换
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
// go语言中,可以直接这样实现换值,不需要中间变量
// 把a,b 赋值给b,a
b, a = a, b
fmt.Println(a, b)
}
变量的本质
变量名是给开发人员看的,它本质是一个内存地址,声明变量的时候,会开辟一块内存空间,值存到内存空间中
匿名变量
匿名变量的特点是一个下划线”__” , 本身就是一个特殊的标识符,称为空白标识符,它可以像其他标识符那样用于变量的声明、赋值,但任何赋给这个标识符的值都将被抛弃,因此不能用于后续代码中使用,也不可以使用这个标识符作为变量对其他变量进行赋值与运算。
可以理解是一个黑洞,丢进去的东西都不见了
匿名变量不分配内存空间
package main
import "fmt"
func test() (int, int) {
return 100,200
}
func main(){
a,_ := test() // 只需要第一个返回值,所以第二个变量定义为匿名变量
_,b := test() // 只需要第二个返回值,所以第一个变量定义为匿名变量
fmt.Println(a,b)
}
// 场景
// 登录之后,返回用户信息,有60个字段
变量的作用域
-
局部变量
函数体内的变量称之为局部变量
-
全局变量
函数体为声明的变量称之为全局变量,在当前go文件中生效
全局变量与局部变量可以使用同一个名字,就近原则会使用局部变量,打印内存地址,会发现内存地址不一样
// 全局变量必须使用var关键字,不能使用:=短变量声明
var name string = "kuangsheng"
func main(){
var name string = "lisu"
fmt.Println(name) // 打印局部变量
}
func aa(){
fmt.Println(name) // 打印全局变量
}
// 就近原则,自上而下
3、常量
const 关键字
不能被修改的变量,const标识符表示这个是常量
const identifier [type] = value
真的无法修改码?
Go语言底层的约定,实际上它是可以修改的,需要跳过常量名,直接找到内存地址,进行修改值
举例
// 显示类型定义
const b string = "abc"
// 隐式类型定义
const b = "abc"
const URL = "www.baidu.com"
const c_name1,c_name2 = value1,value2 // 多个相同类型的常量声明
package main
import "fmt"
func main(){
// 建议大家使用大写字母来定义常量,区分于普通变量
const URL ="www.baidu.com"
const a,b,c = 3.14,"kuangshen",false
fmt.Println(URL)
// 常量声明后,可以不使用
// fmt.Println(a,b,c)
// 常量不能获取内存地址
// fmt.Printf("%p",&URL)
}
执行结果:
www.baidu.com
3.14 kuangshen false
常量和变量放置的内存地址不同,栈、堆、常量池(编译阶段就把常量存放到常量池了)
程序正常的执行,是压栈的过程,常量字符串是存储在静态存储区的,内容不能修改
特殊常量iota
特殊常量,可以认为是一个被编译器修改的常量,它是go语言的常量计数器
iota在const关键字出现时将被重置为0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次,可以用作枚举值
const(
a = iota
b = iota
c = iota
)
第一个iota等于0,每当iota在新的一行被使用时,它的值被自动加1,所以a=0,b=1,c=2,简写
const(
a = iota
b
c
)
举例:
iota会经常出现在源码中
4、基本数据类型
-
动态类型编程语言
-
静态类型编程语言
如Go,在开发的时候,就需要给一些定义的变量赋值空间大小,Go的基本数据类型
go 语言是一种静态类型的编程语言,在go中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才申请大内存,就可以充分利用内存。编译器在进行编译的时候,就要知道每个值的类型,这样编译器就知道要为这个值分配多少内存,并且知道这段分配的内存表示什么
布尔型
package main
import "fmt"
func main() {
var isFlag bool // 默认值为false
isFlag = true
isFlag = 1 // 会报错
fmt.Println(isFlag)
// 格式化输出,占位符:%d整数,%s字符串,%p内存地址,%t 布尔值 %T 数据类型
fmt.Printf("%T,%t\n",isFlag,isFlag) // %T 输出变量的数据类型
}
执行结果:
true
bool,true
数字类型
整型int 和浮点型float32、float64 ,支持复数。
在计算机中,
序号 | 类型和描述 |
---|---|
uint8 | 无符号8位整数(0到255) |
uint16 | 无符号16位整数(0到65535) |
uint32 | 无符号32位整数(0到4294967295) |
uint64 | 无符号64位整数 |
int8 | 有符号8位整数 (-128到127) |
int16 | 有符号16位整数 (-32768到32767) |
int32 | 有符号32位整数 (-2147483648到2147483647) |
int64 | 有符号64位整数 () |
float32 | 32位浮点数容易造成精度丢失,尽量使用float64 |
float64 | 64位浮点数 |
package main
import "fmt"
func main() {
var age int = 18 // 默认int不指定就是使用int64
fmt.Printf("%T,%d\n", age, age)
// 定义一个浮点数
var money float32 = 3.14 // 32位浮点数容易造成精度丢失,一般使用float64
// 浮点数,%f 默认保留6为小数
fmt.Printf("%T,%f\n", money, money) // 默认打印6位小数
fmt.Printf("%T,%.2f\n", money, money) // 打印2位小数
fmt.Printf("%T,%.1f\n", money, money) // 打印1位小数,四舍五入
}
执行结果
验证浮点数计算不精确
package main
import "fmt"
func main() {
//var i2 uint8
//i2 = -100
// 浮点数: 符号位 + 指数位 + 尾数位(存储过程中,可能会丢失,造成精度损失)
// float64的空间 》 float32
var num1 float32 = -123.0000901
var num2 float64 = -123.0000901
fmt.Println(num1)
fmt.Println(num2)
// 结论:使用float来计算,可能导致数据不精确
}
执行结果:
-123.00009
-123.0000901
以下列出常用数字类型的别名
- byte 类似uint8
- rune 类似int32
- int 类似int64
func main() {
var num3 byte = 255
fmt.Printf("%T\n", num3)
var num4 rune = 1000000000
fmt.Printf("%T\n", num4)
// int 根据系统的大小32位默认int32, 64位默认int64
var num5 int = 130483405804804850
fmt.Printf("%T\n", num5)
}
执行结果:
uint8
int32
int
重点记住 byte、int、float
字符与字符串
拓展编码表
Go语言中,所有的字符串都是由单个字符连接起来的
import "fmt"
func main() {
// 单引号 字符
v1 := 'A'
v2 := "A"
fmt.Printf("%T,%s\n", v1, v1)
fmt.Printf("%T,%s\n", v2, v2)
}
执行结果:
int32,%!s(int32=65)
string,A
字符A无法用%s打印,因为它不是字符串,打印出65是ASCII码表中大小字母A的值
func main() {
// 单引号 字符
v1 := 'A'
v2 := "A"
fmt.Printf("%T,%d\n", v1, v1) // 字符A打印的ASCII码表的值,大写字母A的值是65
fmt.Printf("%T,%s\n", v2, v2)
}
执行结果:
int32,65
string,A
百度ASCII码表的每个字符的值
打印中文
func main() {
// 单引号 字符
// 扩展
// 所有的中国字的编码表:GBK
// 全世界的文字s编码表:Unicode编码表
v3 := '中'
fmt.Printf("%T,%d\n", v3, v3)
}
执行结果:
int32,20013
总结:单个字符对应的是编码表的整形值
字符串连接与转义字符
// 字符串连接 +
fmt.Println("hello " + ",kuangshen")
// 转义字符 \
fmt.Println("hello\"kuangshen")
fmt.Println("hello\nkuangshen") // n 换行
fmt.Println("hello\tkuangshen") // t 制表符,4个空格=tab
··
执行结果:
hello ,kuangshen
hello"kuangshen
hello
kuangshen
hello kuangshen
所有的转义字符,百度
数据类型转换
由于go语言不存在隐式类型转换,因此所有类型的转换都必须是显式的声明,语法:
valueOfTypeB = typeB (valueOfTypeA)
import "fmt"
func main() {
a := 3
b := 5.0
// 类型转换
c := float64(a)
// 转换 bool 整型是不能转换为bool
// e := bool(a)
d := byte(a)
fmt.Printf("%T\n", a)
fmt.Printf("%T\n", b)
fmt.Printf("%T\n", c)
//fmt.Printf("%T\n", e)
fmt.Printf("%T\n", d)
}
总结: 每种数据类型分配的内存空间大小是不一样的
6种运算符
下面6种:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
算术运算符
下表列出了所有go语言的算术运算符,假定A值为10,B值为20
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | 双元运算符,由两个变量构成运算 |
- | 相减 | |
* | 相乘 | |
/ | 相除 | |
% | 求余/取模 | B%A输出结果0 |
++ | 自增 | A++输出结果11,单元运算符 |
– | 自减 | A–输出结果9 |
import "fmt"
func main() {
a := 10
b := 3
fmt.Println(a + b)
fmt.Println(a - b)
fmt.Println(a * b)
fmt.Println(a / b)
fmt.Println(a % b)
a++
fmt.Println(a)
a--
fmt.Println(a)
}
执行结果:
13
7
30
3
1
11
10
关系运算符 ,结果都是bool 布尔值
下表列出了所有go语言的关系运算符,假定A值为10,B值为20
运算符 | 描述 | 实例 |
---|---|---|
== | A==B 为false | |
!= | A!=B 为true | |
> | A>B 为false | |
< | A<B 为true | |
>= | A>=B 为false | |
<= | A<=B 为true |
逻辑运算符
下表列出了所有go语言的逻辑运算符,假定A值为true,B值为false
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑AND运算符,如果两边的操作数都是true,则条件true,否则false | (A && B) 为false |
|| | 逻辑OR运算符,如果两边的操作数有一个true,则条件true,否则false | (A || B) 为true |
! | 逻辑NOT运算符, 如果条件为true,则逻辑NOT条件false,否则true | !(A && B) 为true |
位运算符(二进制)0与1 ,加密解密场景
下表列出了所有go语言的位算符,假定A值为60,B值为13
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符“&”是双目运算符,都是1结果为1,否则0 | A&B 结果为12,二进制0000 1100 |
| | 按位或运算符“|”是双目运算符,都是0结果为0,否则1 | A|B结果为61,二进制0011 1101 |
^ | 按位异或运算符”^”是双目运算符,不同则为1,相同为0 | A^B结果为49,二进制0011 0001 |
&^ | 位清空,a&^b,对于b上的每个数值,如果为0,则取a对应位上的数值,如果为1,则取0 | A&^B结果为48,二进制为00110000 |
« | 左移运算符,左移n位就是乘以2的n次方,其功能把«左边的运算数的各二进制全部左移若干位,由«右边的数字指定移动的位数,高位丢弃,低位补0 | A«2 结果位240,二进制为11110000 |
» | 右移运算符,右移n位就是除以2的n次方,其功能把“«”左边的运算数的各二进制全部右移若干位,由“«”右边的数字指定移动的位数,高位补0 | A»2结果为15,二进制为00001111 |
func main() {
// 二进制 0 1 逢二进1 ,1~20的二进制表示如下
// 0 1 电路问题
// 1, 1
// 2, 10
// 3,11
// 4,100
// 5,101
// 6, 110
// 7, 111
// 8, 1000
// 9, 1001
// 10, 1010
// 11, 1011
// 12, 1100
// 13, 1101
// 14, 1110
// 15, 1111
// 16, 10000
// 17, 10001
// 18, 10010
// 19, 10011
// 20, 10100
// 位运算: 二进制的0 false, 1 true
// & 我和你 1 1 结果才是1,否则 0
// | 或运算符 都是0结果为0,否则1
// ^ 不同为1,相同为0
// 60 0011 1100
// 13 0000 1101
// -------------
// & 0000 1100
// | 0011 1101
// ^ 0011 0001
// 结果 12
}
go代码:
var a uint = 60
var b uint = 13
var c uint = 0
c = a & b
fmt.Printf("%d,二进制%b\n", c, c) // 00001100
c = a | b
fmt.Printf("%d,二进制%b\n", c, c) // 61,二进制111101
c = a << 2
fmt.Printf("%d,二进制%b\n", c, c) // 240,二进制11110000
fmt.Printf("%d,二进制%b\n", a, a) // 60,二进制111100
c = a >> 2
fmt.Printf("%d,二进制%b\n", c, c) // 15,二进制1111
执行结果:
12,二进制1100
61,二进制111101
240,二进制11110000
60,二进制111100
15,二进制1111
平时用的少,底层加密解密可以使用,比如说用二进制进行加密,往前移3位,就变成乱码了,往后移3位,变成正常文本,
比如有一段代码,exe文件,底层都是二进制的文件,进行与、或位移操作就相当于加密了
赋值运算符
其他运算符
获取键盘输入
go语言跟c++一样由指针的概念,指向内存地址
var x int
var y float64
// 定义两个变量,键盘录入这两个变量值
fmt.Println("请输入两个数,1、整数,2、浮点数")
// 变量取内存地址 &变量
// Scanln阻塞等待你的键盘输入,然后换行
fmt.Scanf() // 接收格式化输入
fmt.Scanln(&x, &y) // 把输入值保存到内存地址指向的内存空间
fmt.Println("x:", x)
fmt.Println("y:", y)
执行结果
请输入两个数,1、整数,2、浮点数
1 3.14
x: 1
y: 3.14
5、格式化输出占位符
- %s 打印字符串
- %c 输出字符
- %d 打印整形
- %t 打印布尔型
- %f 打印浮点数
- %T 打印变量类型
- %p 打印内存地址
- %v 以默认的方式打印变量的值
- %T 打印变量的类型
- %s 正常输出字符串
- %t 打印true或false
- %p 带0x的指针
- %d 整型
- %#p 不带0x的指针
- %o 不带零的八进制
- %#o 带零的八进制
- %x 小写的十六进制
- %X 大写的十六进制
- %#x 带0x的十六进制
- %U 打印Unicode字符
- %#U 打印带字符的Unicode
- %b 打印整型的二进制
- %q 字符串带双引号,字符串中的引号带转义符
- %f (=%.6f) 6位小数点
- %e (=%.6e) 6位小数点(科学计数法)
- %g 用最少的数字来表示
- %.3g 最多3位数字来表示
- %.3f 最多3位小数来表示