Skip to content

Commit

Permalink
0325
Browse files Browse the repository at this point in the history
  • Loading branch information
ffhelicopter committed Mar 25, 2023
1 parent c3b419f commit a39a4f0
Show file tree
Hide file tree
Showing 68 changed files with 25,147 additions and 210 deletions.
419 changes: 209 additions & 210 deletions SUMMARY.md

Large diffs are not rendered by default.

File renamed without changes.
156 changes: 156 additions & 0 deletions content/42_02_datatype.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# 《Go语言四十二章经》第二章 数据类型

作者:ffhelicopter(李骁)

在 Go 语言中,数据类型可用于参数和变量声明。

## 2.1 基本数据类型

Go 语言按类别有以下几种数据类型:

* 布尔型:<br>
布尔型的值只可以是常量 true 或者 false。一个简单的例子:`var b bool = true`

* 数字类型:<br>
整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。

* 字符串类型:<br>
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。

* 派生类型:<br>
包括:


(a) 指针类型(Pointer)
(b) 数组类型
(c) 结构类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface)
(h) Map 类型

### 数字类型:

Go 也有基于架构的类型,例如:int、uint 和 uintptr,这些类型的长度都是根据运行程序所在的操作系统类型所决定的。


|类型|符号|长度范围|
|:--|:--|:--|
|uint8 |无符号 |8位整型 (0 到 255)|
|uint16 |无符号 |16位整型 (0 到 65535)|
|uint32 |无符号 |32位整型 (0 到 4294967295)|
|uint64 |无符号 |64位整型 (0 到 18446744073709551615)|
|int8 |有符号 |8位整型 (-128 到 127)|
|int16 |有符号 |16位整型 (-32768 到 32767)|
|int32 |有符号 |32位整型 (-2147483648 到 2147483647)|
|int64 |有符号 |64位整型 (-9223372036854775808 到 9223372036854775807)|


## 浮点型:

主要是为了表示小数,也可细分为float32和float64两种。浮点数能够表示的范围可以从很小到很巨大,这个极限值范围可以在math包中获取,math.MaxFloat32表示float32的最大值,大约是3.4e38,math.MaxFloat64大约是1.8e308,两个类型最小的非负值大约是1.4e-45和4.9e-324。


float32大约可以提供小数点后6位的精度,作为对比,float64可以提供小数点后15位的精度。通常情况应该优先选择float64,因此float32的精确度较低,在累积计算时误差扩散很快,而且float32能精确表达的最小正整数并不大,因为浮点数和整数的底层解释方式完全不同。

|类型|长度|
|:--|:--|
|float32 |IEEE-754 32位浮点型数|
|float64 |IEEE-754 64位浮点型数|

## 其他数字类型:

|类型|长度|
|:--|:--|
|byte |类似 uint8|
|rune |类似 int32|
|uint32 |或 64 位|
|int |与 uint 一样大小|
|uintptr |无符号整型,用于存放一个指针|

## 字符串:
只读的Unicode字节序列,Go语言使用UTF-8格式编码Unicode字符,每个字符对应一个rune类型。一旦字符串变量赋值之后,内部的字符就不能修改,英文是一个字节,中文是三个字节。

```go
stringintint, err := strconv.Atoi(string)
stringint64int64, err := strconv.ParseInt(string, 10, 64)
intstringstring := strconv.Itoa(int)
int64stringstring := strconv.FormatInt(int64, 10)
```

而一个range循环会在每次迭代时,解码一个UTF-8编码的符文。每次循环时,循环的索引是当前文字的起始位置,以字节为单位,代码点是它的值(rune)。

使用range迭代字符串时,需要注意的是range迭代的是Unicode而不是字节。返回的两个值,第一个是被迭代的字符的UTF-8编码的第一个字节在字符串中的索引,第二个值的为对应的字符且类型为rune(实际就是表示unicode值的整形数据)。

```go
const s = "Go语言"
for i, r := range s {
fmt.Printf("%#U%d\n", r, i)
}
```
程序输出:

U+0047 'G' : 0<br>
U+006F 'o' : 1<br>
U+8BED '语' : 2<br>
U+8A00 '言' : 5<br>

## 复数:
复数类型相对用的很少,主要是数学学科专业会用上。分为两种类型 complex64和complex128 前部分是实体后部分是虚体。

|类型|长度|
|:--|:--|
|complex64 |32位实数和虚数|
|complex128 |64位实数和虚数|

## 2.2 Unicode(UTF-8)

你可以通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF),以及使用 e 来表示 10 的连乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)

不过 Go 同样支持 Unicode(UTF-8),因此字符同样称为 Unicode 代码点或者 runes,并在内存中使用 int 来表示。在文档中,一般使用格式 U+hhhh 来表示,其中 h 表示一个 16 进制数。其实 rune 也是 Go 当中的一个类型,并且是 int32 的别名。

在书写 Unicode 字符时,需要在 16 进制数之前加上前缀 \u 或者 \U。

因为 Unicode 至少占用 2 个字节,所以我们使用 int16 或者 int 类型来表示。如果需要使用到 4 字节,则会加上 \U 前缀;前缀 \u 则总是紧跟着长度为 4 的 16 进制数,前缀 \U 紧跟着长度为 8 的 16 进制数。

```go
var ch int = '\u0041'
var ch2 int = '\u03B2'
var ch3 int = '\U00101234'
```

## 2.3 复数
Go 拥有以下复数类型:

complex64 (32 位实数和虚数)<br>
complex128 (64 位实数和虚数)<br>

复数使用 re+imi 来表示,其中 re 代表实数部分,im 代表虚数部分,i 为虚数单位。
示例:

```go
var c1 complex64 = 5 + 10i
fmt.Printf("The value is: %v", c1)// 输出: 5 + 10i
```
如果 re 和 im 的类型均为 float32,那么类型为 complex64 的复数 c 可以通过以下方式来获得:

```go
c = complex(re, im)
```
函数 real(c) 和 imag(c) 可以分别获得相应的实数和虚数部分。



[目录](https://github.com/ffhelicopter/Go42/blob/master/SUMMARY.md)

[第一章 Go安装与运行](https://github.com/ffhelicopter/Go42/blob/master/content/42_01_install.md)

[第三章 变量](https://github.com/ffhelicopter/Go42/blob/master/content/42_03_var.md)



>本书《Go语言四十二章经》内容在github上同步地址:https://github.com/ffhelicopter/Go42
>
>
>虽然本书中例子都经过实际运行,但难免出现错误和不足之处,烦请您指出;如有建议也欢迎交流。
216 changes: 216 additions & 0 deletions content/42_03_var.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# 《Go语言四十二章经》第三章 变量

作者:ffhelicopter(李骁)

## 3.1 变量以及声明

Go 语言中有四类标记:标识符(identifiers),关键字(keywords),运算符(operators )和标点符号(punctuation)以及字面量(literals) 。

Go 语言变量标识符由字母、数字、下划线组成,其中首个字母不能为数字,同一字母的大小写在Go语言中代表不同标识,注意区分A 和a 是不同的标识。

根据Go语言规范,标识符命名程序实体,例如变量和类型。 标识符是一个或多个Unicode字母和数字的序列。 标识符中的第一个字符必须是Unicode字母。标识符:

```go
identifier = letter { letter | unicode_digit } .
```

Go语言规范中,下划线“_”也被认为是字母:

```go
The underscore character _ (U+005F) is considered a letter.
letter = unicode_letter | "_" .
unicode_digit = /* a Unicode code point classified as "Number, decimal digit" */ .
```

在Unicode标准8.0中,第4.5节“常规类别”定义了一组字符类别。 Go语言将Unicode中任何字母类别Lu,Ll,Lt,Lm或Lo中的所有字符视为Unicode字母,将数字类别Nd中的字符视为Unicode数字。

据统计,Go语言视为Unicode的字母(含下划线_)一共20871个,这里面包括中文,详情见表:

|字母类别 |含义 |数量|
|:--|:--|:--|
|Lu |字母,大写 |1781|
|Ll |字母,小写 |2145|
|Lt |字母,词首字母大写 |31|
|Lm |字母,修饰符 |250|
|Lo |字母,其他 |16053|
|Nd |数字,十进制数 |610|

一般习惯上,在Go语言命名标识符时,我们还是选择英文的52个大小写字母以及0-9数字和下划线来组合成合适的标识符。上表中其他的字符也可以用于标识符,但不在上表中的字符是不能用在Go语言标识符中。后面我们提到大写字母,主要是指Lu类别中的1781个字母。

另外,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|

Go语言变量声明使用关键字var,下面我们声明了几个变量:

```go
var (
a int
b bool
str string
浮点 float32 // 没错,中文可以作为变量标识符
)
```

这种因式分解关键字的写法一般用于声明全局变量,一般在func 外定义。

当一个变量被var声明之后,系统自动赋予它该类型的零值:

* int 为 0
* float 为 0.0
* bool 为 false
* string 为空字符串""
* 指针为 nil

记住,这些变量在 Go 中都是经过初始化的。

多变量可以在同一行进行赋值,也称为 并行 或 同时 或 平行赋值。如:

```go
a, b, c = 5, 7, "abc"
```

简式声明:

```go
a, b, c := 5, 7, "abc" // 注意等号前的冒号
```
右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。

简式声明一般用在func内,要注意的是:全局变量和简式声明的变量尽量不要同名,否则很容易产生偶然的变量隐藏Accidental Variable Shadowing。

即使对于经验丰富的Go开发者而言,这也是一个非常常见的陷阱。这个坑很容易挖,但又很难发现。

```go
func main() {
x := 1
fmt.Println(x) // prints 1
{
fmt.Println(x) // prints 1
x := 2
fmt.Println(x) // prints 2
}
fmt.Println(x) // prints 1 (不是2)
}
```
如果你想要交换两个变量的值,则可以简单地使用:

```go
a, b = b, a
```
(在 Go 语言中,这样省去了使用交换函数的必要)

空白标识符 _ 也被用于抛弃值,如值 5 在:``_, b = 5, 7`` 中被抛弃。

```go
_, b = 5, 7
```
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。

由于Go语言有个强制规定,在函数内一定要使用声明的变量,但未使用的全局变量是没问题的。为了避免有未使用的变量,代码将编译失败,我们可以将该未使用的变量改为 _

另外,在Go语言中,如果引入的包未使用,也不能通过编译。有时我们需要引入的包,比如需要init(),或者调试代码时我们可能去掉了某些包的功能使用,你可以添加一个下划线标记符,_,来作为这个包的名字,从而避免编译失败。下滑线标记符用于引入,但不使用。

```go
package main

import (
_ "fmt"
"log"
"time"
)

var _ = log.Println
func main() {
_ = time.Now
}
```
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:

```go
val, err = Func1(var1)
```
对于布尔值的好的命名能够很好地提升代码的可读性,例如以 is 或者 Is 开头的 isSorted、isFinished、isVisible,使用这样的命名能够在阅读代码的获得阅读正常语句一样的良好体验,例如标准库中的 unicode.IsDigit(ch)。

在 Go 语言中,指针属于引用类型,其它的引用类型还包括 slices,maps和 channel。

注意,Go中的数组是数值,因此当你向函数中传递数组时,函数会得到原始数组数据的一份复制。如果你打算更新数组的数据,可以考虑使用数组指针类型。

```go
package main

import "fmt"

func main() {
x := [3]int{1, 2, 3}

func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) // prints &[7 2 3]
}(&x)

fmt.Println(x) // prints [7 2 3]
}
```
被引用的变量会存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间。

>引申:
>
>编译器会做逃逸分析,所以由Go的编译器决定在哪(堆or栈)分配内存,保证程序的正确性。
## 3.2 零值nil
nil 标志符用于表示interface、函数、maps、slices、channels、error、指针等的“零值”。如果你不指定变量的类型,编译器将无法编译你的代码,因为它猜不出具体的类型。

```go
package main

func main() {
var x = nil // 错误

_ = x
}
```
在一个 nil 的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic:

```go
package main

func main() {
var m map[string]int
m["one"] = 1 //error

}
```
字符串不会为 nil

这对于经常使用 nil 分配字符串变量的开发者而言是个需要注意的地方。

```go
var str string = "" // ""是字符串的零值
```
根据前面的介绍,其实这样写和上面的效果一样:

```go
var str string
```


[目录](https://github.com/ffhelicopter/Go42/blob/master/SUMMARY.md)

[第二章 数据类型](https://github.com/ffhelicopter/Go42/blob/master/content/42_02_datatype.md)

[第四章 常量](https://github.com/ffhelicopter/Go42/blob/master/content/42_04_const.md)



>本书《Go语言四十二章经》内容在github上同步地址:https://github.com/ffhelicopter/Go42
>
>
>虽然本书中例子都经过实际运行,但难免出现错误和不足之处,烦请您指出;如有建议也欢迎交流。
Loading

0 comments on commit a39a4f0

Please sign in to comment.