Go 1.19.4 语法基础-Day 02

1. 注释

1.1 多行注释

1.1.1 方式一(不推荐使用)

package main

/* 多行注释
  test函数的作用
  参数a类型和作用
  参数b类型和作用
  参数c类型和作用
*/
func test1(a int, b string, c bool){

}

在这里插入图片描述

1.1.2 方式二(推荐)

go的源码库中也是使用这种多行注释方式

package main

// test函数的作用,
// 参数a类型和作用,
// 参数b类型和作用,
// 参数c类型和作用.
func test2(a int, b string, c bool){

}

在这里插入图片描述

1.2 单行注释

package main

import "fmt"

func main() {
	// 单行注释
	fmt.Println("fmt") // 打印什么的
}

1.3 TODO 提示功能没有完成

1.3.1 安装插件 todo tree

在这里插入图片描述

1.3.2 最终效果
package main

import "fmt"

func main() {
	// TODO: 还有一个功能待完成
	fmt.Println("fmt")
}

在这里插入图片描述

类似的还有:
// NOTE: 请注意
// Deprecated: 告知已经过期,建议不要使用。未来某个版本可能移除
不过todo比较常用。

1.4 注释总结

(1)函数、结构体等习惯把注释写在函数或结构体上面
(2)包注释会写在package之上

2. 命名规范

(1)标识符采用CamelCase驼峰命名法

  • 如果只在包内可用,就采用小驼峰命名(userName)
  • 如果要在包外可见(另一个包中可见),就采用大驼峰命名(UserName),也被称为包级别的全局变量或者导出变量。
  • 大小驼峰在包内外都能用,但是如果要在包外可见,就必须用大驼峰。

(2)简单循环变量可以使用i、j、k、v等,就是单独的字母也能使用。

(3)条件变量、循环变量可以是单个字母或单个单词,Go倾向于使用单个字母。

(4)常量驼峰命名即可

  • 在其他语言中,常量多使用全大写加下划线的命名方式,Go语言没有这个要求。
  • 对约定俗成的全大写,例如PI。

(5)函数/方法的参数、返回值应是单个单词或单个字母。

(6)函数可以是多个单词命名。

(7)类型可以是多个单词命名。

(8)方法由于调用时会绑定类型,所以可以考虑使用单个单词。

(9)包以小写单个单词命名,包名应该和导入路径的最后一段路径保持一致。

(10)接口优先采用单个单词命名,一般加er后缀。Go语言推荐尽量定义小接口(就是接口中的功能尽可能的少),最后用若干个小接口来组成一个大接口。

3. 关键字

官网:​​https://golang.google.cn/ref/spec

// 所谓的关键字就是go程序用的(语言保留字),我们开发过程中不能用关键字命名。
break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

4. 预定义标识符

官网:​​https://golang.google.cn/ref/spec#Predeclared_identifiers

// 预定义标识符可以用,但是不建议使用,会出问题。
Types:
 any bool byte comparable
 complex64 complex128 error float32 float64
 int int8 int16 int32 int64 rune string
 uint uint8 uint16 uint32 uint64 uintptr

Constants:
 true false iota

Zero value:
 nil

Functions:
 append cap close complex copy delete imag len
 make new panic print println real recover

5. 标识符

在编程中,标识符(Identifier)是用来表示变量、常量、函数、类型、接口或其他用户定义的实体的名称。标识符可以被用于命名各种程序元素,以便在代码中引用和识别它们。

在Go语言中,标识符的命名规则如下:

  • 一个名字,本质上是个字符串,用来指代一个值。
  • 只能是大小写字母、数字、下划线,也可以是Unicode字符
  • 不能以数字开头
  • 不能是Go语言的关键字
  • 尽量不要使用“预定义标识符”,否则后果难料
  • 大小写敏感

标识符建议:

  • 不要使用中文
  • 非必要不要使用拼音
  • 尽量遵守上面的命名规范,或形成一套行之有效的命名规则

总结:

  • 标识符是专门给开发人员用的,用来指代内存中的一个值,如a = 123。
  • 标识符在编译过后,就看不到了,因为被替换成了内存地址,这个内存地址中,存放着对应的值,如123。

5.1 字面常量

字面常量是值。

5.1.1 字面常量含义

在Go语言中,字面常量(Literal Constants)是直接写在代码中的固定值,无需使用变量声明。对于整数、浮点数、布尔值、字符串、空值等,都可以作为字面常量。

所谓字面的意思,就是你一看就知道这是什么类型的数据,它是值,但不是标识符。
但你不可以去修改这个值,因为它是常量,只有变量才可以修改。
对于能不能修改,取决于你如何定义标识符,如下:
如a := 100,这个100就是一个数值类型的字面常量,而a只是一个标识符,同时a也是一个变量,那么此时,100在程序运行过程中是可变的。

又如:const b = 200,这里的200依然是一个数值类型的字面常量,b也依然是标识符,但同时,由于使用了const,所以b也是一个常量,所以这里的200,在程序运行过程中不可变。

5.1.2 字面常量示例

/* 数值常量
// 数值类的字面常量
100
0x6162 0x61_62_63
3.14
3.14e2
3.14E-2

// 字符(rune)类的字面常量
// 字符不管是多少个组成,如'xxx',都视为一个字符
'测'
'\u6d4b'
'\x31'
'1'
'\n'
*/数值常量

/* 字面常量
// 字符串类的字面常量
"abc" "\x61b\x63"
"测试" "\u6d4b试"
"\n"
*/字面常量


// 其他类型的字面常量
tue
false
iota

5.2 常量

5.2.1 什么是常量

什么是常量?
首先它是一个标识符,这个标识符有一个值,但这个值在程序运行过程中是不可改变的,如const a = 100,这个a就是常量,100为常量值。

是在其他语言中,指的是元素地址不可变,内容可变。
但在go中,要求更加严格,要求的是内容都不能变。

常量:使用const定义一个标识符,它所对应的值,不允许被修改。
对常量并不要求全大写加下划线的命名规则。

在定义常量时,它的值只能是字面常量,如果是其他值,就直接报错了

5.2.2 常量示例

5.2.2.1 单个常量定义
const a int = 100 // 指定类型定义并赋值
5.2.2.2 多个(批量)常量定义
const ( // “无类型常量untyped constant”定义,推荐
	b = "abc" // 不写数据类型时,go会根据值自动判断常量是什么类型。
	c = 12.3
	d = 'T'
)
5.2.2.3 错误的定义方式
// 错误,const定义常量时,必须在定义时赋值,并且之后不能改变值,换一种说法就是常量在定义时,必须被初始化,=赋值就是初始化。
const a

// 错误,数组的容器内容{1, 2}会变化,凡是不能在编译期间明确地确定下来的,在go中是不被允许被定义为常量的。
const c = [2]int{1, 2}

5.3 iota

5.3.1 iota介绍

在Go语言中,iota 是一个被预定义的无类型整数常量,它用于在常量声明中生成一系列递增的值。

在每个const关键字出现时,iota会被重置为0(但它本身也是从0开始),然后在每个连续的常量声明中逐步递增。

5.3.2 单iota演示

package main

import "fmt"

func main() {
	const a = iota // 0,因为有const
	const b = iota // 0,因为有const
	fmt.Println(a, b)
}
=================调试结果=================
0 0

5.3.3 多iota演示

批量iota操作的时候,才会出现递增效果,0、1、2……

func main() {
	const ( // 多iota递增
		c = iota // iota=0
		d        // iota=1
		e        // iota=2
		_        // 下划线为特殊标识符,在go中可以用来做标识符,但是不能使用它,如print _,会报错。iota=3
		_        // 也可以叫空白标识符或匿名变量。iota=4
		f        // iota=5
		g = iota // iota=6
		h        // iota=7
	)
	fmt.Println(c, d, e, f, g, h)
}
=================调试结果=================
0 1 2 5 6 7

代码变化一

package main

import "fmt"

func main() {
	const ( // 多iota递增
		c = iota
		d
		e
		_
		_
		f
		// 下面开始变化
		g = iota + 10 // iota=6+10=16
		h             // iota=7,但是会复用上面iota+10的公式,也就是iota=7+10=17
	)
	fmt.Println(c, d, e, f, g, h)
}
=================调试结果=================
0 1 2 5 16 17

代码变化二

package main

import "fmt"

func main() {
	const ( // 多iota递增
		c = 5 // 改变这里,会发现直到g,都是5,因为一个多iota一旦被定义,不管位置在哪儿,都会从第一行开始
		d
		e
		_
		_
		f
		g = iota + 10
		h
		i = 20
		j
		k = iota
		l
	)
	fmt.Println(c, d, e, f, g, h, i, j, k, l)
}
=================调试结果=================
5 5 5 5 16 17 20 20 10 11

_下划线 是空白标识符
下划线和其他标识符使用方式一样,但它不会分配内存,不占名词空间
为匿名变量赋值,其值会被抛弃,因此,后续代码中不能使用匿名变量的值,也不能使用匿名变量为其他变量赋值。
说白了就是只能用_占位,不能调用它。

5.3.4 iota应用场景

枚举类型:​​iota​​可以用于创建枚举类型的常量集。例如,可以使用​​iota​​来定义一周中每一天的常量。

const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

5.4 变量

5.4.1 变量介绍

变量:赋值后,可以改变值的标识符,但是对于go这种强类型语言来说,不能换数据类型,比如a = 1,然后下面又a = “a”。
建议采用驼峰命名法。

定义方式:
(1)长格式定义
var a = 100

(2)短格式定义
a := 100

5.4.2 变量定义示例

5.4.2.1 单个变量定义
package main

import (
	"fmt"
)

func main() {
	//var a // 错误的声明方式。声明变量时,必须要指定数据类型

	var a int // 正确的声明方式。没有指定具体的变量值时,会自动初始化一个零值(整数0),因为go中不允许有空值的变量。
	fmt.Println(a)

	var b = 200 // 正确的声明方式。没有指定具体的数据类型时,声明(var b)+初始化(b = 200)后,go会自动推导出值的数据类型
	fmt.Println(b)
    
}
=================调试结果=================
0
200
5.4.2.2 多个变量定义
func main() {
	//var a int, b int // 错误的声明方式

	var a, b int // 正确的声明方式。批量同类型合并
	fmt.Println(a, b)

	var ( // 正确的声明方式
		c int = 300
		d int
	)
	fmt.Println(c, d)

	var g, h int = 100, 200 // 正确的声明方式
	fmt.Println(g, h)

	var ( // 正确的声明方式
		m int
		n int
		t string = "abc"
	)
	m, n = 300, 400
	fmt.Println(m, n, t)

	m, n = n, m // 交换写法。但是要注意Go语言的多重赋值是同时执行的
	fmt.Println(m, n)
    
    //var a = 100 // 错误的声明方式,因为上面a已经被var声明过了
    a = 100 // 正确的声明方式
}
=================调试结果=================
0 0
300 0
100 200
300 400 abc
400 300
5.4.2.3 多变量定义的注意事项

Go语言的多重赋值是同时执行的

5.4.2.4 短格式变量定义

推荐使用这种方式来声明变量,但是它只能在函数内部使用,不能用来全局变量。

使用 := 定义变量并立即初始化
只能用在函数中,不能用来定义全局变量、不能提供数据类型,具体的数据类型由编译器来推断

func main() {
	a := 100 // 正确的声明方式。:=,表示声明变量+定义变量
	//a := 200 // 不能这样!因为上面已经声明和定义过了,不能重复声明定义。
	a = 300 // 可以这样,单纯的赋值。
	fmt.Println(a)
}
=============
300

5.5 零值

变量已经被声明,但是未被显式初始化(var a int),这个变量将会被设置为零值。
var a int = 100
声明:var a int
初始化:= 100

其它语言中,只声明未初始化的变量误用非常危险,但是,Go语言却坚持“零值可用”理念。

  • int为0
  • float为0.0
  • bool为false
  • string为空串""(注意是双引号)
  • 指针类型为nil
  • 其它类型数据零值,学到再说

5.6 变量作用域

5.6.1 包级标识符(全局)

在Go语言中,在.go文件中的顶层代码中(函数体外部),定义的标识符称为包级标识符。
全局变量可以在整个包或者包外被使用,如果首字母大写,可在包外可见。如果首字母小写,则包内可见。

// 无类型常量定义(包外可见)
var A = 20   // int
var B = 3.14 // float64

// 无类型常量定义(包内可见)
var a = 20   // int
var b = 3.14 // float64
// 指定类型
var a int32 = 20
var b float32 = 3.14

// 延迟初始化需要指定类型,用零值先初始化,因为不给类型,不知道用什么类型的零值
// 有相同关系的声明可以使用同一批定义
var (
 name string
 age  int
)

使用建议:
(1)顶层代码中定义包级标识符

  • 首字母大写作为包导出标识符,首字母小写作为包内可见标识符。
  • const定义包级常量,必须在声明时初始化
  • var定义包级变量,可以指定类型,也可以使用无类型常量定义,延迟赋值必须指定类型,不然没法确定零值。

(2)有相关关系的,可以批量定义在一起

(3)一般声明时,还是考虑“就近原则”,尽量靠近第一次使用的地方声明

(4)不能使用短格式定义

5.6.2 局部标识符

定义在函数体内部,包括main函数,这些标识符就是局部标识符。
使用建议:

  • ​在函数中定义的标识符
  • const定义局部常量
  • var定义局部变量,可以指定类型,也可以使用无类型常量定义,延迟赋值必须指定类型,不然没法确定零值。
  • 有相关关系的,可以批量定义在一起。
  • 在函数内,直接赋值的变量多采用短格式定义。

5.6.3 形式参数

函数定义中的变量称为形式参数。

func sum(a, b int) int { // 这里的ab就是形式参数
 xxx
}

6. 布尔型

类型bool,定义了2个预定义常量,分别是true、false。
在其他语言中,布尔型可以和其他类型的数据进行运算,但是在go中,不可以的,bool就是bool。

6.1 布尔表达式符号

逻辑与:&&
逻辑或:||
逻辑非:!
运算符:==、!=、>、<、>=、<=

7. 数值型

官网文档:​​https://golang.google.cn/ref/spec#Numeric_types​

7.1 整型

(1)长度不同有符号
int8(1个字节)、int16(C语言short,2个字节)、int32(4个字节)、int64(C语言long,8个字节)
(2)长度不同无符号
uint8(1个字节)、uint16(2个字节)、uint32(4个字节)、uint64 (个字节)。byte类型,它是uint8的别名
(3)自动匹配平台:int、uint

int类型它至少占用32位,但一定注意它不等同于int32,不是int32的别名。要看CPU,32位就是4字节,64位就是8字节。但是也不是说int是8字节64位,就等同于int64,它们依然是不同类型!

7.1.1 什么是有符号和无符号

这里用int8(1字节)举例

首先这里介绍下进制,如在10进制中,是看不到10的,因为计数是从0开始,到9,当计数超过9时,需要使用一个1和一个0来表示10。同理,其他进制也相同。

那么如何理解有无符号呢?
首先说下有符号,这里先以二进制为例:
如00000001,首先最左边属于最高位,最右边属于最低位,1byte=8bits,1个字节,就是8个位,每个位置上只有2种可能,也就是0或1,这就是二进制。
二进制中,用最高位的0或1来表示正负符号,其中0为正,1为负。那么如下:
10000001,这个二进制的最高位就是-1(负符号)。

那么无符号呢?
和有符号相反,最高位不表示符号。
如00000001,就是1,因为它没有符号位,所有位都用于表示非负整数。
为啥是1?
其实这里是涉及到了进制转换计算,首先在进制中,是由权重一说的。
如二进制,它的权重底数是2,权重从右到左依次是:1、2、4、8、16、32等等以此类推。
那么二进制00000001转换成十进制方式就是如下图:
在这里插入图片描述

7.1.2 有无符号整型范围

7.1.2.1 有符号

计算方式:
取值范围 = -(2^(n-1)) 到 (2^(n-1))-1,其中n为类型的位数:如int8,n就是8。
​​-(2^(8-1))​​​ = ​​-128​​
​​2^(8-1)-1​​​ = ​​2^7​​ = 128 -1 = 127

数据类型位数占用内存空间取值范围
int8​​81字节-128 到 127
int16​​162字节-32,768 到 32,767
int32​​(别名rune)324字节-2,147,483,648 到 2,147,483,647
int64​​648字节-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
7.1.2.2 无符号

计算方式:
取值范围=0~2^n-1,n为位数。

数据类型位数占用内存空间取值范围
uint8​​(别名byte)​​81字节0 到 255
uint16​​162字节0 到 65,535
uint32​​324字节到 4,294,967,295
uint64​​648字节0 到 18,446,744,073,709,551,615

7.1.3 Go中的进制前缀表示法

前缀对于编译器解析数字文字非常重要。
如果没有前缀,编译器会将数字解释为十进制数。

进制类型前缀
二进制0b(b可以大写可小写)
八进制0或0o(可以省略,但是不要,避免歧义,o可以大写也可以小写)
十进制默认无前缀
十六进制0x(X可以大写也可以小写)
十六进制字符‘\x’

例如:
二进制数:​​0b1010​​
八进制数:​​010​​​、​​0o10​​
十进制数:​​10​​
十六进制数:​​0x10​​

7.1.4 实际代码中的运用

package main

import "fmt"

func main() {
	a := 0x20 // 0x为16进制,表示20是个16进制类型
	fmt.Println(a) // 这里Println会把16进制自动转换为10进制并输出结果
}
=================调试结果=================
32

7.1.5 查看对应值的类型(格式化输出)

package main

import "fmt"

func main() {
	a := 0x20
	fmt.Println(a)

	b := 0b100000
	fmt.Println(b) // Println= print line,打印完后换行

	var c = 40
	d := a + b + c
	// Printf=print format,格式化输出,不换行。
	// %T对应第一个d,%d对应第二个d。
	// %T表示type类型。%d表示digital数字。
	// 也就是把d的数据类型和对应的值打印出来。
	fmt.Printf("%T %d", d, d)
    fmt.Printf("%T %v", d, d) // 当不能确定值的类型时,可以使用%v,v表示value,就是一个占位符,适用于任何场景。
}
=================调试结果=================
32
32
int 104
int 104

7.1.6 Go不支持跨类型计算说明

package main

import "fmt"

func main() {
	a := 100 // 这种不指定数据类型的,自动识别为int,int会根据当前的系统类型,来使用32位还是64位
	var b int64 = 100 // 这里就指明了int64,但是注意,假设上面的int自动识别为int64也和这里的int64不同。
	// a + b // 这里可以使用a+b,然后鼠标移到上面,会看到一个报错:invalid operation: a + b (mismatched types int and int64)compilerMismatchedTypes,
	// 意思是a是int,b是int64,不同数据类型不能相加。
    // 但是可以通过强制类型转换,来实现上面的需求。

}

7.1.7 强制类型转换

7.1.7.1 int和长度不同有符号互转
package main

import "fmt"

func main() {
	a := 100                // int
	var b int64 = 100       // int64
	fmt.Println(a + int(b)) // 直接把b的int64转换成了int
    fmt.Println(int64(a) + b) // 这样也可以,只要数据类型相同就行
}
=================调试结果=================
200
200
7.1.7.2 int转string

int转string会有一个黄色下划线警告提示:大概意思是说把一个int转成了字符串。不影响代码运行。
当把int类型强制转换为string时,Go 语言会将该整数解释为一个 ASCII 码值,并返回相应的字符。

package main

import "fmt"

func main() {
	var a = 50
    fmt.Printf("%T %[1]v\n", string(a)) // %v是比较万能的数据类型显示,字符串的类型为%S。
    // %[1]v,表示的是索引:"%T索引默认为1,对应string(a),%v默认对应第二个值,但是这里并没有,所以修正索引为1
}
=================调试结果=================
string 2 // 当把int类型强制转换为string时,Go 语言会将该整数解释为一个 ASCII 码值,并返回相应的字符。
7.1.7.3 int转其他类型
package main

import "fmt"

func main() {
	var a = 50
	fmt.Printf("%T %[1]v\n%T %[2]v\n%T %[3]f\n", string(a), rune(a), float32(a))
}
=================调试结果=================
package main

import "fmt"

func main() {
	var a = 50
	fmt.Printf("%T %[1]v\n%T %[2]v\n%T %[3]f\n", string(a), rune(a), float32(a))
}
=================调试结果=================
string 2 // 字符串,实际应该是"2",但是从int转到str显示的是ascll码值
int32 50 // 字符,实际应该是'50'
float32 50.000000 // 浮点数

在这里插入图片描述

7.1.8 为什么要分有符号和无符号及自适应

首先说说自适应,自适应int和uint,纯粹是为了方便,当内存充足时,可以使用该方式,也是工作中大多数时使用的方式。

然后是有无符号,需要精细使用内存时,就要根据实际情况来选择不同的有无符号整型。

实际工作中,优先考虑的是程序运行速度快、无BUG,其次才是内存占用优化。

7.2 字符和整数

7.2.1 什么是字符

字符是在计算机系统中表示单个书写符号(例如字母、数字或符号)的基本单位。它是一个抽象概念,可以由不同的字符编码(例如 ASCII、Unicode)表示。

ASCII范围:0-127
Unicode范围:100万字符以上

字符’'使用Unicode编码,并且Unicode是包含ASCII码的。

在Go中,字符表达,必须使用单引号引住一个字符,如:‘a’、‘1’、‘@‘等。
但是在计算机中,字符也可以用数字来表示,如ASCII码表。
ASCII码表中的内容,是某个数字映射后的某个字符,那到底是数字还是字符呢?取决于我们编程时如何使用。
比如97,我们可以单纯的把它看成是一个整型数字,但是如果提前限定它的类型为string字符类型,那么此时对照ASCII码表,97对应的字符就是’a’,这个时候实际上就是操作的这个字符’a’,而不是97.

计算机种只有二进制的数据,到底要怎么展示出来,取决于我们定义为什么类型的数据。

type byte = uint8 // byte定义为uint8的别名,1个字节
type rune = int32 // rune定义为int32的别名,4个字节,可以是Unicode字符

type myint int // 这种是定义新类型,不是定义别名
package main

import "fmt"

func main() {
	var a = '测' // 由于是中文,所以会先去查询Unicode表,然后返回对应的值27979
	fmt.Printf("%T %[1]v %[1]c\n", a)

	b := '2'                          // 将字符 '2' 的 ASCII 码(即二进制00110010=十进制50)赋值给变量 b。
	fmt.Printf("%T %[1]v %[1]c\n", b) // %c表示打印一个字符。此时2在rune类型下就应该是2,虽然rune就是int32
	// 换句话说,就是内存中'2'=50 int32,但只要我们打印的时候,把它当成字符,那输出出来就是字符'2'。
	// 内存中的数据到底应该是什么类型,取决于我们赋予它什么样的类型。

	// 总结:如果b := 'value',这个'value'字符字面量,那默认就是rune也就是int32,4字节.

	// 但实际上,存储50使用无符号1字节就可以了,如下:
	var c byte = '2' // 定义类型只能用var,不能用:=
	fmt.Printf("%T %[1]d %[1]c\n", c)
}
=================调试结果=================
int32 27979int32 50 2
uint8 50 2

特别注意:字符串在内存中使用utf-8,rune输出是unicode。

7.3 浮点数

float32:最大范围约为3.4e38,通过math.MaxFloat32查看
float64:最大范围约为1.8e308,通过math.MaxFloat64查看
打印格式化符常用%f

package main

import "fmt"

func main() {
	f := 3.1415 // 默认类型:float64
	fmt.Printf("%T %[1]v %[1]f", f) // v:默认格式。f:浮点数,默认小数点后六位
}
=================调试结果=================
float64 3.1415 3.141500

7.3.1 浮点数格式化打印

package main

import "fmt"

func main() {
	f := 3.1415
	fmt.Printf("|%3f|\n", f)     // 打印宽度为3,但是由于f的值长度已经超过了3,所以看着没有变化
	fmt.Printf("|%20f|\n", f)    // 打印宽度为20,其中f占8个位置,剩下的12个位置在左边
	fmt.Printf("|%-20f|\n", f)   // 加个-,就表示左对齐,默认为右对齐
	fmt.Printf("|%-20.3f|\n", f) // 左对齐,并显示小数点后3位,并且会自动四舍五入
	fmt.Printf("|%.3f|\n", f)    // 只显示小数点后3位,没有对齐要求,并且会自动四舍五入
}
=================调试结果=================
|3.141500|
|            3.141500|
|3.141500            |
|3.142               |
|3.142|

7.3.2 浮点型与无符号整数字面量运算

前面说过,go中不同类型的数据不能互相运算,但是,当一个数字没有明确定义类型时,它此时是无符号的整数字面量,是可以和其他整型进行运算的。

package main

import "fmt"

func main() {
	f := 3.1415 // 默认类型:float64
	fmt.Println(f + 100) // 这个100就是无符号的整数字面量,编译器会根据另一个变量的类型,自动对它做隐式转换。
	// 也就是f是浮点,100也会被转换为浮点,从而完成计算。
}
=================调试结果=================
103.1415

8. 转义字符

转义字符是一种特殊的字符序列,以反斜杠()开头,后跟一个字符,用来表示一些特殊的含义。在Golang中,常见的转义字符包括: ​​(换行符)、​​ ​​(制表符)、​​'​​(单引号)、​​"(双引号)等。通过使用转义字符,我们可以在字符串中表示一些特殊的字符和控制字符。
具体常见类型如下:

转义字符Unicode字符的十六进制数字序列作用
\aU+0007当解释器遇到转义字符 \a 时,它会发出哔哔声。
\bU+0008表示退格(BS)字符。当解释器遇到转义字符\b时,它会将光标向左移动一格,覆盖前一个字符。
\fU+000C表示换页(FF)字符。当解释器遇到转义字符\f时,它会将光标移动到当前页面的顶部,并清除页面上的所有文本。
\nU+000A换行符,将光标移动到下一行的开头,从而开始新的一行。
\rU+000D回车符,将光标移动到当前行的行首,单独使用看不到效果。
\tU+0009横向制表符,相当于2个tab,也就是8个空格。
\vU+000B垂直制表符
\\双斜杠U+005C\会转义紧随其后的字符
\’U+0027单引号,当解释器遇到转义字符’时,它会插入一个单引号字符到字符串中。
\"U+0022双引号,作用同上。

9. 字符串

使用双引号或反引号引起来的任意个字符。它是字面常量。

9.1 定义字符串的方式

package main

import "fmt"

func main() {
	a := ""   // 定义一个空串
	a = "abc" // 这里表面看是abc覆盖了"",实际上:a最开始在内存中指向""的地址,后面重新指向了"abc",所以""和"abc"都是独立的个体,不违背常量不可修改的特性。最终""会被丢弃。
	fmt.Println(a)
}
=================调试结果=================
abc

9.2 结合转义字符使用

9.2.1 "

package main

import "fmt"

func main() {
	a := ""
	// 这里由于引号的特殊性,必须用\把"转义成普通字符串才能成功打印,不然"""就会引起界定符冲突问题。
	a = "ab\"c"
	fmt.Println(a)

	a = "ab'c" // 双引号中可以包含单引号,不会引起界定符冲突的问题
	fmt.Println(a)

	a = `json: "name"` // 通过反引号的方式,也可以避免界定符冲突
	fmt.Println(a)
}

=================调试结果=================
ab"c
ab'c
json: "name"

9.2.2 \t

package main

import "fmt"

func main() {
	a := ""
	a = "ab\tc"
	fmt.Println(a)
}
=================调试结果=================
ab	c

9.2.3 \r\n

package main

import "fmt"

func main() {
	a := ""
	a = "ab\r\n\tc"
	fmt.Println(a)

	a = `ab
	c` // 该方式也可以表示上面的\r\n\t
	fmt.Println(a)
}
=================调试结果=================
ab
	c
ab
	c

9.3 字符串拼接

package main

import "fmt"

func main() {
	a := ""
	a = "123" + "xyz" // a="123"是一个常量,不能改变,内存中还有一个xyz,这俩组合起来,诞生了一个新的字符串"123xyz",并不是覆盖率原来的a
	fmt.Println(a)
}
=================调试结果=================
123xyz

9.4 字符串格式化

格式符参考fmt包帮助 ​​https://pkg.go.dev/fmt​​

9.4.1 字符串

格式化符号作用
%v打印变量对应的值。不同的类型,产生不同的输出内容
%+v对于结构体,会打印出字段名和值
%#v对于结构体,有更加详细的输出
%T打印值的类型
%%打印百分号本身

9.4.2 整数

格式化符号作用
%b整型以二进制方式显示
%o整型以八进制方式显示
%O八进制带0o前缀
%x十六进制小写
%X十六进制大写
%U把一个整数用Unicode格式打印。例如 fmt.Printf(“%U, %x, %c\n”, 27979, 27979,27979) 输出 U+6D4B, 6d4b
%c把rune、byte的整型值用字符形式打印
%q把一个整型当做Unicode字符输出,类似%c,不过在字符外面多了单引号。q的意思就是quote。

9.4.3 浮点数

格式化符号作用
%e、%E科学计数法
%f、%F小数表示法,最常用
%g内部选择使用%e还是%f以简洁输出
%G选择%E或%F

9.4.4 字符串或字节切片

格式化符号作用
%s字符串输出。如果是rune切片,需要string强制类型转换
%q类似%s,外部加上双引号。q的意思就是quote

9.4.5 指针

格式化符号作用
%p指针,十六进制方式显示

9.4.6 总结

在这里插入图片描述

9.5 特殊格式写法

package main

import "fmt"

func main() {
	a, b, c, d := 100, 200, 300, 400
    // %[2]v,这个2可以省略的,因为索引号是递增的
	fmt.Printf("%d, %[2]v, %[1]d, %d", a, b, c, d)
}
=================调试结果=================
100, 200, 100, 200

在这里插入图片描述

9.6 Print函数

9.6.1 输出到标准输出

(1)Print:接受任意数量的参数,使用空格分割,并将它们转换为字符串后连接在一起,然后输出到控制台。
(2)Println:同上,最后追加换行
(3)Printf:按照指定的格式符输出

9.6.2 输出到字符串

经常用来拼接字符串用
(1)Sprint:相当于Print,不过输出为string
(2)Sprintln:相当于Println,不过输出为string
(3)Sprintf:相当于Printf,不过输出为string

package main

import "fmt"

func main() {
	a := '测'
	// fmt.Sprintf("%c", a)的作用并不是直接输出到控制台,而是输出为一个字符串,要想使用,必须用一个变量接住它
	b := fmt.Sprintf("%c", a)
	fmt.Println(b) // 输出变量b的值到控制台

	b = fmt.Sprintf("%c%s", a, "xyz") // 也可以这样实现字符串拼接
	fmt.Println(b)
}
=================调试结果=================
测
测xyz

10. 操作符

参考:https://golang.google.cn/ref/spec#Operators_and_punctuation

10.1 逻辑运算真值表

逻辑运算中,只有真和假,也就是bool中的true和false。
1为真,0为假。

10.1.1 与逻辑

可以理解为乘法或者理解为双方都为真结果才为真

ABF
000(假)
010(假)
100(假)
111(真)
10.1.2 或逻辑

可以理解为加法或理解为任意一方为真既最终结果为真

ABF
000(假)
010(真)
100(真)
111(真)

10.1.3 非逻辑

这个就是取反

AF
01(真)
10(假)

10.2 算数运算符

主要就是+、-、*、/、%、++、–。

10.2.1 单目运算符(+、-、++、–)

上面说到了运算符种类,其中+、- 还可以当做正负用,不过就不是算数运算符了,而是单目运算符。

package main

import "fmt"

func main() {
	// 这里的-,代表的是负号,不是减号,并且-只作用在5上,这种就是单目运算符
	fmt.Println(5/2, -5/2)
}
=================调试结果=================
2 -2 // 整数除法丢弃余数,只返回整数
package main

import "fmt"

func main() {
	a := 5
    // 注意:a++只是一个语句,并不是表达式,所以不能像这里这样定义,会报错
	//fmt.Println(5/2, -a/2, a++)
	a ++ // 正确的定义方式(不可以++a)。a ++=a+=1=a=a+1
	fmt.Println(a)
}
=================调试结果=================
6

10.2.2 双目运算符

双目运算符其实指的是运算符作用在双方,比如5/2,/需要的是除数和被除数,这种就是双目运算符。

package main

import "fmt"

func main() {
	a := 5
	fmt.Println(5/2, -a/2)
}
=================调试结果=================
2 -2

10.2.3 常量计算问题

常量分为有类型常量和untyped无类型常量。
不同类型的常量是不能相互运算的(除非强制类型转换),但是无类型常量之间可以相互运算。

package main

import "fmt"

func main() {
	a := 5
	b := 3.5
	fmt.Println(a * b) // 这种是不可以的,因为5是int,b是float64
    
    // 这样可以,因为1和2.3都属于无符号字面常量
    // 其实也就是语法糖,编译器自动做了隐式类型转换,但是隐式类型转换往往是往精度更大的转,所以这里会转为浮点。
    // 1为无符号整型常量,2.3为无符号浮点型常量
    fmt.Println(1 * 2.3) // 官方称为无类型常量
    
    // 或者这样也行
    c := 1 * 2.5 // 官方称为无类型常量
    fmt.Println(c)
    fmt.Printf("%T %v\n", c, c)
}
=================调试结果=================
2.3
3.5
float64 3.5

10.3 位运算符

主要是这几个:
&位与、|位或、异或(二进制同为0,异为1)、&位清空、<<、>>

10.3.1 &位与(按位与运算)

应用场景:如判断奇偶数。

package main

import "fmt"

func main() {
	a := 0b10          // 0b10二进制对应十进制2
	fmt.Println(a & 1) // 2 与 1,与就是按位做乘法,实际的运算过程如下:
	// 2 0b 1 0
	//      * *
	// 1 0b 0 1
	// ----------
	//      0 0
	// 所以,a & 1 = 0
}
=================调试结果=================
0

10.3.2 |位或(按位或运算)

按位或运算,如果两个位都为 0,则结果为 0;否则,结果为 1。

package main

import "fmt"

func main() {
	a := 0b10          // 0b10二进制对应十进制2
	fmt.Println(a | 1) // 2 或 1,或就是按位做加法,但也并不完全是加法,0+0=0,其他都为1,实际的运算过程如下:
	// 2 0b 1 0
	//      + +
	// 1 0b 0 1
	// ----------
	//      1 1
	// 二进制0b11转十进制为3
	// 所以,a | 1 = 3
}
=================调试结果=================
3

10.3.3 >>(右移位)

package main

import "fmt"

func main() {
	fmt.Println(2 >> 2) // 把2向右移动2位。过程如下:
	// 2对应的二进制:0010,向右移动1位:0001,向右移动2位:0000 1,这个1已经没有位置了,所以为0
}
=================调试结果=================
0

10.3.4 &^位清空

&^位清空可以理解为&位与得出结果的取反值。
重点记住这句话:&^位清空的运算方式以第二个数为标准: 第二个数字的位值为1,则结果的对应位清零;如果第二个数字的位值为0,则结果的对应位采用第一个数的位值。

package main

import "fmt"

func main() {
	a := 0b1001
	fmt.Println(a & 3)
	fmt.Println(a &^ 3)
	// &^位清空逻辑如下
	// 首先是a & 3
	// 1 0 0 1
	// * * * *
	// 0 0 1 1
	// 0 0 0 1 = 1
	// 然后到a &^ 3,运算方式以第二个数为标准: 第二个数字的位值为1,则结果的对应位清零;如果第二个数字的位值为0,则结果的对应位采用第一个数的位值。
	// 1 0 0 1
	// 0 0 1 1
	// 1 0 0 0 = 8
}
=================调试结果=================
1
8
package main

import "fmt"

func main() {
	fmt.Println(15 & 5)
	// 15 1111
	// 5  0101
	// 5  0101
	fmt.Println(15 &^ 5)
	// 15 1111
	// 5  0101
	// 10 1010
}
=================调试结果=================
5
10

10.4 比较运算符

比较运算符组成的表达式,返回bool类型值。
成立返回True,不成立返回False。
主要是以下类型:
==、!=、>、<、>=、<=
但是注意:不同数据类型不能进行比较。

package main

import "fmt"

func main() {
	fmt.Printf("%T, %[1]v", "ABC" == "abc")
}
=================调试结果=================
bool, false

10.5 逻辑运算符

由于Go语言对类型的要求,逻辑运算符操作的只能是bool类型数据,那么结果也只会是bool型。
主要是一下几个:
&&、||、!

逻辑运算符优先级
(1)!:最高
(2)&&:第二
(3)||:最低

10.5.1 &&

与运算中,有短路这个概念,就是第一个条件为假的话,就触发短路,结束了。
所以实际运用中,可以把短路条件配置在最前面,失败就停止运行,减少运算量。

package main

import "fmt"

func main() {
	fmt.Println(123 > 234 && "abc" > "ABC")
	// 逻辑真值表中,只有真真为真,其他都为假。
	// 所以123 > 234为假,触发了逻辑运算中的短路,就直接结束了,不会再执行"abc" > "ABC"了。
}
=================调试结果=================
false

10.5.2 ||

package main

import "fmt"

func main() {
	// 逻辑真值表中,或逻辑中只要有一方为真,结果就为真。
	fmt.Println(123 > 234 || "abc" > "ABC") // 字符串的比较,实际上对比的是ASCII码值
	// a=97,b=98,c=99
	// A=65,B=66,C=67
}
=================调试结果=================
true
10.5.2.1 使用||时的注意事项
package main

import "fmt"

func main() {
	fmt.Println(1 || "abc" > "ABC") // 注意这样是不可以的
    // 在逻辑运算中,GO要求操作数必须是bool类型,否则不能逻辑运算,并且不提供隐式转换。
}

10.6 赋值运算符

符号含义示例
=将等号右边的值,赋值给等号左边的标识符var a = 1
:=短格式赋值a := 1
+=将等号右边的值相加,得出的结果赋值给等号左边的标识符var a += 1
var a = a + 1
-=将等号右边的值相减,得出的结果赋值给等号左边的标识符var a -= 1
var a = a - 1
*=将等号右边的值相乘,得出的结果赋值给等号左边的标识符var a *= 1
var a = a * 1
/=将等号右边的值相除,得出的结果赋值给等号左边的标识符var a /= 1
var a = a / 1
%=将等号右边的值取余,得出的结果赋值给等号左边的标识符var a %= 1
var a = a % 1
>>=右位移后赋值var a >>= 1
var a = a >> 1
<<=左位移后赋值var a <<= 1
var a = a << 1
&=按位与后赋值var a &= 1
var a = a & 1
&^=位清空后赋值var a &^= 1
var a = a &^ 1
^=位异或后赋值(二进制同为0,异为1)var a ^= 1
var a = a ^ 1
|=位或后赋值var a |= 1
var a = a | 1

10.7 三元运算符

Go 中没有三元运算符。
没有 ?: 的原因是,语言的设计者看到这个操作经常被用来创建难以理解的复杂表达式。
在替代方案上,if-else 形式虽然较长,但无疑是更清晰的。
一门语言只需要一个条件控制流结构。

10.8 指针操作

数据是放在内存中,内存是线性编址的。任何数据在内存中都可以通过一个地址来找到它。
&:取出内存地址
*:解析内存地址(就是把地址中的内容拿出来),表示通过指针取值
10.8.1 & 取出内存地址

10.8.1 & 取出内存地址

package main

import "fmt"

func main() {
	a := 100
	b := &a // 注意:这里的&是一个单目运算符,不是位与双目运算符。主要就是为了取出a的内存地址。
	fmt.Println(b)
}
=================调试结果=================
0xc000120058 // 这就是100对应的内存地址。

10.8.2 * 解析内存地址

package main

import "fmt"

func main() {
	a := 100
	b := &a

	c := *b
	fmt.Println(c)
}
=================调试结果=================
100

10.8.3 问题思考:上述赋值后,所有变量的内存地址是否相同

package main

import "fmt"

func main() {
	a := 100 // a指向100,并且100的内存地址为0xc0000b6058
	b := &a // b指向100的内存地址0xc0000b6058,相当于b = 0xc0000b6058
	fmt.Println(b)
    // 但是,*b,是把内促地址下的值拿出来了,就相当于c = 100
    // c = 100,会在系统中重新开辟一块内促来存储这个100,让c指向这个新的内存地址
    // 所以b == &c不成立
	c := *b 
	fmt.Println(a == c, b == &c)
    
    var d = a // d 指向100
    // a和b都指向100,所以a == d
    // b指向0xc0000b6058,但是d也是在一个新的内存地址中来存储100的,所以b == &d不成立
	fmt.Println(a == d, b == &d)
}
=================调试结果=================
0xc0000b6058
true false
true false

10.9 运算符优先级

表中优先级由高到低。
单目 > 双目。
算数 > 移位 > 比较 > 逻辑 > 赋值。
搞不清,用括号,避免产生歧义
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/683808.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Aigtek高压放大器在纳米材料中的应用研究

随着纳米材料科学的迅速发展&#xff0c;纳米材料在各个领域中的应用也逐渐扩展。而高压放大器作为一种重要的电子元件&#xff0c;在纳米材料研究中起着至关重要的作用。下面将介绍高压放大器在纳米材料研究中的应用以及相关的研究进展。 高压放大器是一种能够将输入信号放大到…

【论文精读】DCRNN-扩散图卷积循环神经网络

DCRNN 模型是南加州大学的 Li 等人发表在 I C L R 2018 ICLR 2018 ICLR2018 会议上一个用于交通预测的时空预测模型&#xff0c;论文题目为: 《DIFFUSION CONVOLUTIONAL RECURRENT NEURAL NETWORK: DATA-DRIVEN TRAFFIC FORECASTING》&#xff0c;文章地址为: https://arxiv.o…

算法导论实战(三)(算法导论习题第十六章)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;算法启示录 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 前言 算法导论的知识点学习将持续性更新在算…

ARM32开发--GPIO输入模式

知不足而奋进 望远山而前行 目录 文章目录 前言 浮空输入 上拉输入 下拉输入 模拟输入 总结 前言 在数字电路设计和嵌入式系统开发中&#xff0c;理解输入信号的处理方式对确保系统稳定性和可靠性至关重要。不同的输入处理方式包括上拉输入、下拉输入、浮空输入和模拟输…

【C#学习笔记】属性和字段

文章目录 前言属性和字段的区别字段访问修饰符和关键字定义变量类型的定义变量命名变量的赋值 属性 不同的使用情况 前言 最近在工作的过程中常常会觉得自己在程序设计方面的能力还是有欠缺。例如一直对于变量的声明感到不足&#xff0c;在工作中为了图方便总是直接public定义…

Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:无人机自主飞行软件平台

案例简介 北京泛化智能科技有限公司&#xff08;gi&#xff09;所主导开发的 Generalized Autonomy Aviation System (GAAS) 是为无人机以及城市空中交通 (UAM, Urban Air Mobility) 所设计的开源无人机自主飞行框架。通过 SLAM、路径规划和 Global Optimization Graph 等功能…

骨传导耳机有哪些是值得入手的?看完这篇推荐就懂了!

骨传导耳机在运动圈非常的受欢迎&#xff0c;因为佩戴运动的时候&#xff0c;骨传导耳机能够稳固佩戴&#xff0c;无论是跳跃或者是摇晃身体等&#xff0c;耳机都不会轻易掉落&#xff01;而很多朋友对于骨传导耳机总是想尝试却又害怕掉坑&#xff01;于是为了给大家提供更多的…

分布式事务Seata中XA和AT模式介绍

Seata中XA和AT模式介绍 分布式事务介绍分布式解决方案解决分布式事务的思路Seata的架构Seata中的XA模式Seata的XA模型流程XA模式优缺点实现XA模式 Seata中的AT模式Seata中的AT模式流程实现AT模式AT模式优缺点 AT模式与XA模式的区别 分布式事务介绍 分布式事务&#xff0c;就是…

HCIA-RS基础-VLAN配置

目录 前言创建拓扑创建VLAN查看创建的VLAN配置trunk口并放行VLAN配置access接口查看所有vlan基本信息测试网络连通性命令合集 前言 VLAN定义&#xff1a;VLAN是一种将局域网内的设备从逻辑上划分成一个个网段&#xff0c;从而实现虚拟工作组的新兴数据交换技术。VLAN优点&…

移动硬盘读不出来?5个解决技巧公开!

“不知道为什么&#xff0c;我的移动硬盘突然就读不出来了&#xff0c;大家有什么方法可以更好的读取移动硬盘吗&#xff1f;希望大家帮帮我&#xff01;” 在数字化日益盛行的今天&#xff0c;移动硬盘已成为我们存储和携带大量数据的重要工具。然而&#xff0c;当这个“数据仓…

HashMap第2讲——put方法源码及细节

上篇文章介绍了HashMap在JDK 1.8前后的四大变化&#xff0c;今天就进入到put方法的源码解析。HashMap的设计非常巧妙&#xff0c;细节也很多&#xff0c;今天来看看部分细节&#xff0c;后续的文章会一一介绍。 ps&#xff1a;学习源码的目的不仅仅是为了了解它的运行机制&…

idea的代码没有提交到仓库怎么撤回到本地?

代码已经提交到变更列表但是还没有push推送到仓库上&#xff0c;可以用这个方法 点击日志-右键要撤回的记录-选择撤销提交 撤销的又回到本地变更 当然你只能撤销自己提交的&#xff0c;别人的你撤销不了

AI基础设施是AI落地赋能的核心关键

AI基础设施内涵与特性 以深度落地赋能为导向&#xff0c;AI供给侧持续推进技术要素全面融合、技术能力自主可控、技术服务普惠低成本&#xff0c;AI供给“基建 化”势在必行&#xff0c;AI基础设施正成为AI的关键供给形态。算法、算力、数据是AI技术应用的三大核心支撑要素&am…

SpringBoot+Vue在线视频课程网站(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 用户教师管理员 系统功能截图

Gradio.NET:一个快速制作演示demo网页的利器

Gradio介绍 Gradio是一个用于创建机器学习模型交互界面的Python库。它允许开发者快速为他们的模型创建一个简单的web界面&#xff0c;以便于非技术用户和其他开发者进行交互和测试。 Gradio的主要优点是易用性和灵活性。你只需要几行代码就可以为你的模型创建一个交互界面。你…

【Python数据挖掘实战案例】机器学习LightGBM算法原理、特点、应用---基于鸢尾花iris数据集分类实战

一、引言 1、简要介绍数据挖掘的重要性和应用 在数字化时代&#xff0c;数据已经成为企业和社会决策的重要依据。数据挖掘作为一门交叉学科&#xff0c;结合了统计学、机器学习、数据库技术和可视化等多个领域的知识&#xff0c;旨在从海量数据中提取有价值的信息&#xff0c…

Marvelous Designer中一些棉质布料预设

Marvelous Designer中一些棉质布料预设的解释&#xff1a; Cotton_14_Wale_Corduroy&#xff1a;14条细鲸鱼纹的灯芯绒&#xff0c;适合制作温暖且有质感的服装。Cotton_40s_Chambray&#xff1a;40支精梳针织的府绸布&#xff0c;通常用于制作休闲衬衫。Cotton_40s_Poplin&am…

echars饼图、柱状图 java返回的数据格式

1、echars饼状图返回的数据格式 [ { "name": "A", "value": 3 }, { "name": "B", "value": 2 }, { "name": "C", "value": 2 } ] java代码Demo 为例&#xff1a;根据名字分组&…

vuInhub靶场实战系列--prime:1

免责声明 本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关。 目录 免责声明前言一、环境配置1.1 靶场信息1.2 靶场配置 二、信息收集2.1 主机发现2.1.1 netdiscover2.1.2 nmap主机扫描2.1.3 arp-scan主机扫描 2.2 端口扫描…

【CMake系列】06-项目结构与输出路径管理

为了对大型项目实现更好的管理【模块化协作开发等等】&#xff0c;cmake 提供了很多指令&#xff0c;可以对项目的结构进行调整、管理&#xff0c;便于项目的合理规划。本文我们要学习的就是 项目结构的设置&#xff0c;以及 构建程序等 输出路径的设置 本专栏的实践代码全部放…