Go全栈第2节:Go基础语法

2022/12/10

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输出变量的类型
}

执行结果:

小结:

  1. 定义变量使用定式,var 变量名称
  2. 给变量赋值,使用变量名赋值

打印内存地址

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种:

  1. 算术运算符
  2. 关系运算符
  3. 逻辑运算符
  4. 位运算符
  5. 赋值运算符
  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位小数来表示

Post Directory