Finger
拎着烈酒背着孤独踟蹰的代码狗

Go-learning

1.语言详解--类型

1.1 变量

变量(variable)表示没有固定值且可以改变的数(数学定义),也可以说是一段或者多段用来存储数据的内存。(计算机系统定义)

作为静态类型语言,go变量总是有固定的数据类型,类型决定了变量内存的长度和存储格式,可以改变变量值(类型转换或指针操作),无法改变类型。

定义

关键字var用于定义变量,类型放在变量名后。运行时内存分配操作会确保变量自动初始化为二进制零值(zero value),避免出现不可控行为。 如显式提供初始化值,可省略变量类型,有编译器推断。

var x int //自动初始化为0
var y = false //自动推断为bool类型
可一次定义多个变量,包括用不同初始值定义不同类型

var x, y int //相同类型的多个变量
var a, s = 100, "abc" //不同类型的初始化值
按照编程习惯,建议以组的方式整理多行变量定义,即用大括号美观一点。

var {
x, y int
a, s =100, "abc"
}
简短模式(short variable declaration)

除var关键词外,还能使用如下模式

func main(){
x := 100
a, s :=1, "abc"
}
简短模式限制

定义变量,同时显式初始化
不能提供数据类型
只能用于函数内部
简短模式不一定总是重新定义变量,也可能是部分退化的赋值操作。 退化赋值的前提条件是 最少有一个新变量被定义且必须是同一作用域。

func main(){
x := 100
println(&x)

x := 200 //错误:no new variable on left side of :=
println(&x, x)
}
输出: 1.png

func main(){
x := 100
println(&x,x)

{x, y := 200, 300 //不同作用域,全部hi新变量定义
println(&x, x, y)
}
}
在处理函数错误返回值时,退化赋值允许我们重复使用err变量

package main

import {
"log"
"os"
}

func main() {
f, err := os.open("/dev/radom")
...

buf := make([]byte, 1024)
n, err := f.read(buf) //err退化赋值,n新定义
}
多变量赋值

先计算出所有右值,再依次完成赋值操作。

func main(){
x, y := 1,2
x, y = y+3, x+2
println(x, y)
}
输出: 1.png

5 3

tips:赋值操作,必须确保左右值类型相同

未使用错误

go语言的神奇之处,编译器讲未使用的局部变量当做错误,虽说知道它的初衷是好的,有助于培养良好的编程习惯。

1.2 命名

每个程序猿都会有自己变量命名习惯,但是还是建议标准化命名。

空标识符

和python类似,go也有一个名为 _ 的特殊成员(blank identifier)。通常作为忽略占位符使用,可做表达式,无法读取内容。

import "strconv"

func main() {
x, _ := strconv.Atoi("12") // 忽略atoi的err返回值
println(x)
}
空标识符可用于临时规避编译器对未使用变量和导入包的错误检查。但他是预置成员,不能重新定义。

1.3 常量

常量就是指运行时不变的值,通常是一些字面量。使用常量就可用一个易于阅读的标识符号来代替“魔法数字”, 也使得在调整常量值时,无需修改所有引用代码。

常量值必须是编译器可确定的字符,字符串,数字或者布尔型。可指定常量类型,或有编译器通过初始化值推断,和之前一样。
可在函数代码块中定义常量,不曾使用的常量不会引发编译错误
显式指定类型,确保常量左右值类型一致,可做显式转换。右值不能超出常量类型取值范围,否则会溢出。
常量值也可以是某些编译器计算出结果的表达式,如 unsafe sizeof len cap等
在常量组中如不能指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同。
例子:

import "fmt"

func main() {
const(
x uint16 = 120
y //与上一行x类型,右值相同
s = "abc"
z //与s的烈性,右值相同
)

fmt.println("%T, %v\n", y, y)
fmt.println("%T, %v\n", z, z)

}
输出:

uint16, 120
string, abc

枚举

go语言没有明确意义上的enum定义,但是可借助iota标识符实现一组自增常量值来实现枚举类型。

const(
x = iota //0
y //1
z //2
)
自增作用范围是常量值,可在多个常量定义中使用多个iota,各自有单独技术,只需确保组中每行常量的列数量相同即可。
如中断iota自增,必须显式恢复。且后续自增值按行序递增,而非c enum 那般按上一取值递增。
自增默认数据类型为int, 可显式指定类型。
在实际编码中,建议用自定义类型实现用途明确的枚举类型。
展开

不同于变量在运行期分配存储内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用。

const y = 0x200

func main() {
println(y)
}
数字常量不会分配存储空间,无需像变量那么通过内存寻址来取值,所以也不会获得地址。

1.4 基本类型

1.png

支持八进制、十六进制以及科学计算书法。标准库math定义了各数字类型的取值范围。
标准库strconv可在不同进制(字符串)间转换。
使用浮点数,需注意小数位的有效精度
别名

在官方的规范中,专门提到有两个别名:

byte alis for uint8
tune alis for int32
别名类型无需转换,可直接赋值。

1.5 引用类型

引用类型(reference type) 特指slice map channel 这三种预定义类型

他们有浮渣的存储结构,除了分配内存外,还需要初始化相关属性。

内置函数 new 计算类型⼤大⼩小,为其分配零值内存,返回指针。而 make会被编译器翻译 成具体的创建函数,由其分配内存和初始化成员结构,返回对象而非指针。

1.6 类型转换

除常量,别名类型以及未命名类型外,go强制要求使用显式类型转换,加上不支持操作符重载。

同样不能将非bool类型结果当做true or false 使用。
语法歧义

如果转换的目标是指针,单向通道或没有返回值的函数类型,那么必须使用括号,以避免语法分解错误。

func main() {
x := 100
p := int(&x) //error : cannot convert &x (type *int) to type int32
// invalid indirect of int(&x) (type int)
}
正确的做法是用括号,让编译器 讲
int解析为指针类型

Point(p) // 相当于 *(Point(p))
(
Point)(p)
<-chan int(c) // 相当于 <-(chan int(c))
(<-chan int)(c)
1.7 自定义类型

可将类型分为命名和未命名两⼤大类。命名类型包括 bool、int、string 等,而array、 slice、map 等和具体元素类型、长度等有关,属于未命名类型。

具有相同声明的未命名类型被视为同一类型。
具有相同基类型的指针。
具有相同元素类型和⻓长度的 array。
具有相同元素类型的 slice。
具有相同键值类型的 map。
具有相同元素类型和传送⽅方向的 channel。
具有相同字段序列 (字段名、类型、标签、顺序) 的匿名 struct。
签名相同 (参数和返回值,不包括参数名称) 的 function。
方法集相同 (方法名、方法签名相同,和次序无关) 的 interface。
未命名类型转换规则

所属类型相同
基础类型相同,且其中一个是未命名类型
数据类型相同,讲双向通道赋值给单向通道,且其中一个为未命名类型
将默认值nil赋值给切片 字典 通道 指针 函数 接口
对象实现了目标接口
x := 1234
var b bigint = bigint(x) // 必须显式转换,除⾮非是常量。
var b2 int64 = int64(b)
var s myslice = []int{1, 2, 3} // 未命名类型,隐式转换。
var s2 []int = s

这篇文章还没有人发言,快抢第一!

发表评论