go程序设计语言第三章-基本数据类型

Go数据类型分为基本类型(basic type)和组合类型(composite type),基本类型有字符串、布尔、数值, 组合类型有:

  • 指针类型,类C指针
  • 结构体类型,类C结构体
  • 函数类型,函数类型在go中是一种一等公民类型
  • 容器类型,包括数组、切片和map
  • 通道类型,用来同步并发的协程
  • 接口类型,在反射和多态中发挥重要作用
    其中指针、切片、字典、函数、通道都属于引用类型。在引用类型中,任一引用类型数据的修改都会影响所有该引用的拷贝。

整形

go中数值类型包括几种大小不同的整数、浮点数和复数。每种类型都决定了对应的大小范围和是否支持正负号。
有符号整数: int8、int16、int32、int64,分别对应8、16、32、64bit大小的有符号整数,
与此对应的是uint8、uint16、uint32、uint64四种无符号整数

还有对应CPU平台机器字大小的有符号int、无符号uint,两种类型都有可能是32或64bit,随不同的编译器、不同的硬件而改变。

Unicode字符rune与int32等价,通常用于表示一个Unicode码点。这两个名称可以互换使用。
同样byte与uint8等价,byte类型用于强调数值是一个原始的数据而不是一个小的整数。(比如字节流,就是一个个byte组成,每个byte都是一个无符号8位整数)

还有一种无符号整数uintptr,没有指定具体的byte大小但足以容纳指针,只有在底层编程时才使用。

int、uint、uintptr是不同于int8、uint8等固定宽度的整数类型。因此int和int32不同,即使int的大小是32bit,在需要将int当作int32类型的地方也需要显式的类型转换。

有符号数采用二进制补码形式表示,一个n位的有符号数表示范围为-2^(n-1)
到 2^(n-1) - 1。无符号位整数的表示范围0到2^n - 1.
下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照优先级递减的顺序排列:
二元运算符的优先级

* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||

二元运算符有5种优先级,同一级内采用左结合规则,可以使用小括号指定优先级。
表格中前两行例如+还有一个与赋值相结合的对应运算符,用于简化赋值语句。

+、-、*和/ 可以适用于整数、浮点数、复数,但是取模%只能用于整数。go中取模操作后符号取决于被取模数。除法运算符
则依赖操作数是否全为整数,如5.0/4.0的结果是1.25,但是5/4的结果是1,因为整数除法会向着0方向截断余数。

一个算术运算的结果,如果需要更多的bit位才能表示,则说明计算结果溢出了。溢出时高位舍弃。

整数类型可以用== != >等用来比较,布尔型、数字类型和字符串等基本类型都是可比较的,也就是说两个相同类型的值可以用==和!=进行比较。

一元的加法和减法运算符:+ 一元加法(无效果) - 负数,对于整数,+x是0+x的简写,-x则是0-x的简写;对于浮点数和复数,+x就是x,-x则是x 的负数。

位运算符
&      位运算 AND
|      位运算 OR
^      位运算 XOR
&^     位清空(AND NOT)
<<     左移
>>     右移

^运算符作为二元时是异或,一元时表示按位取反。
位操作运算符&^用于按位置零(AND NOT):如果对应y中bit位为1的话,表达式z = x &^ y结果z的对应的bit位为0,否则z对应的bit位等于x相应的bit位的值。

var x uint8 = 1<<1 | 1<<5
var y uint8 = 1<<1 | 1<<2

fmt.Printf("%08b\n", x) // "00100010", the set {1, 5}
fmt.Printf("%08b\n", y) // "00000110", the set {1, 2}

fmt.Printf("%08b\n", x&y)  // "00000010", the intersection {1}
fmt.Printf("%08b\n", x|y)  // "00100110", the union {1, 2, 5}
fmt.Printf("%08b\n", x^y)  // "00100100", the symmetric difference {2, 5}
fmt.Printf("%08b\n", x&^y) // "00100000", the difference {5}

for i := uint(0); i < 8; i++ {
    if x&(1<<i) != 0 { // membership test
        fmt.Println(i) // "1", "5"
    }
}

fmt.Printf("%08b\n", x<<1) // "01000100", the set {2, 6}
fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}

左移和右移适用于无符号和有符号数,左移时两者都用0填充右边空缺比特位,但右移时无符号数用0填充左边比特位,而有符号数则使用符号位填充。

无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合。

对于每种类型T,如果转换允许的话,类型转换操作T(x)将x转换为T类型。许多整数之间的相互转换并不会改变数值;它们只是告诉编译器如何解释这个值。
但是对于将一个大尺寸的整数类型转为一个小尺寸的整数类型,或者是将一个浮点数转为整数,可能会改变数值或丢失精度:

浮点数到整数的转换将丢失任何小数部分,然后向数轴零方向截断。

任何大小的整数字面值都可以用以0开始的八进制格式书写,例如0666;或用以0x或0X开头的十六进制格式书写,例如0xdeadbeef。十六进制数字可以用大写或小写字母。

布尔型

只有true和false,可以和&&(AND)和||(OR)操作符结合,并且有短路行为:如果运算符左边值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值.

常量

常量表达式的值在编译期计算,而不是在运行期。每种常量的潜在类型都是基础类型:boolean、string或数字。

const语句声明一个有名字的常量,其值不可更改。
所有常量运算在编译期间完成,当操作数是常量时,一些运行时的错误在编译时被发现。

常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof

一个常量的声明也可以包含一个类型和一个值,但是如果没有显式指明类型,那么将从右边的表达式推断类型。
const a = 0
const a int = 0

字符串

一个字符串是一个不可改变的字节序列。
文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。
内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目)。
字符串可以用==和<进行比较;比较通过逐个字节比较完成的,因此比较的结果是字符串自然编码的顺序。

字符串字面量

字符串值也可以用字符串面值方式编写,只要将一系列字节序列包含在双引号内即可:

"Hello, 世界"

因为Go语言源文件总是用UTF8编码,并且Go语言的文本字符串也以UTF8编码的方式处理,因此我们可以将Unicode码点也写到字符串面值中。
在一个双引号包含的字符串面值中,可以用以反斜杠\开头的转义序列插入任意的数据。下面的换行、回车和制表符等是常见的ASCII控制代码的转义方式:

\a      响铃
\b      退格
\f      换页
\n      换行
\r      回车
\t      制表符
\v      垂直制表符
\'      单引号 (只用在 '\'' 形式的rune符号面值中)
\"      双引号 (只用在 "..." 形式的字符串面值中)
\\      反斜杠

可以通过十六进制或八进制转义在字符串面值中包含任意的字节。
一个十六进制的转义形式是\xhh,其中两个h表示十六进制数字(大写或小写都可以)。
一个八进制转义形式是\ooo,包含三个八进制的o数字(0到7),但是不能超过\377(译注:对应一个字节的范围,十进制为255)。每一个单一的字节表达一个特定的值。

iota常量生成器

常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。
在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一。

无类型常量

Go语言的常量有个不同寻常之处。虽然一个常量可以有任意一个确定的基础类型,例如int或float64,或者是类似time.Duration这样命名的基础类型,但是许多常量并没有一个明确的基础类型。
编译器为这些没有明确基础类型的数字常量提供比基础类型更高精度的算术运算;你可以认为至少有256bit的运算精度。
这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。
通过延迟明确常量的具体类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。

也就是说,自己通过const语句声明的无类型常量(如const a = 10)和运算语句(如fmt.Println(1 + 2)), 这当中的
常量都属于无类型常量。无类型常量参与计算,不用再做类型转换,比如a+10.

对于常量面值,不同的写法可能会对应不同的类型。例如0、0.0、0i和\u0000虽然有着相同的常量值,但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。
同样,true和false也是无类型的布尔类型,字符串面值常量是无类型的字符串类型。

只有常量可以是无类型的,当常量赋值给有类型的变量,会按照变量类型做隐式转换;
当没有显示类型的变量声明时,常量的形式将隐式决定变量的默认类型,例如:

i := 0      // untyped integer;        implicit int(0)
r := '\000' // untyped rune;           implicit rune('\000')
f := 0.0    // untyped floating-point; implicit float64(0.0)
c := 0i     // untyped complex;        implicit complex128(0i)

无类型整数常量转换为int,它的内存大小是不确定的,但是无类型浮点数和复数常量则转换为内存大小明确的float64和complex128。
如果不知道浮点数类型的内存大小是很难写出正确的数值算法的,因此Go语言不存在整型类似的不确定内存大小的浮点数和复数类型。

组合数据类型 Composite type

数组、结构体: 聚合类型,有固定内存大小的数据结构
slice、map:动态的数据结构

数组

固定长度,元素类型相同。
声明:

var a [3]int
aa := [3]int{}
var b [3]int = [3]int{1, 2, 3}
var c [3]int = [3]int{1,2}   // 省略部分元素
var d := [...]int{1, 2, 3}   // 长度由右侧元素个数确定
r := [...]int{99: -1}        // 100个元素,最后一个元素为-1

数组的长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定。

Slice

动态类型,引用底层数组,有三个属性:指针、长度、容量。

  • 生成方式:从数组切片、从slice切片,使用make函数
  • 和数组不同,slice不能直接比较
  • append函数可能会导致底层数组发生变化,因此常常将append的结果赋值给原slice
  • 只声明不初始化则为nil slice,其长度和容量均为0,且没有底层的数组

map

一个无序的字典,key必须是可比较的类型,如整数、字符串等

  • 利用map的key作为一个tuple,则value可以设置为bool类型或者struct{}类型(代表无数据的结构体)
  • map也不能直接比较,唯一的例外是和nil进行比较
  • 只声明不初始话则为nil map

结构体

结构体的每个元素都是一个变量,因此可以取地址
一个命名为S的结构体类型将不能再包含S类型的成员:因为一个聚合的值不能包含它自身。
(该限制同样适用于数组。)但是S类型的结构体可以包含*S指针类型的成员,这可以让我们创建递归的数据结构

如果结构体没有任何成员的话就是空结构体,写作struct{}。它的大小为0,也不包含任何信息,但是有时候依然是有价值的。
有些Go语言程序员用map来模拟set数据结构时,用它来代替map中布尔类型的value,
只是强调key的重要性,但是因为节约的空间有限,而且语法比较复杂,所以我们通常会避免这样的用法。

函数返回一个结构体,则不能使用.形式调用结构体的元素,返回结构体指针则可以。

如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,
那样的话两个结构体将可以使用或!=运算符进行比较。相等比较运算符将比较两个结构体的每个成员。
可比较的机构体可以作为map的key

  • 匿名成员: 当成员只有类型,不指定名字时为匿名成员。使用点号操作符可以直接抵达最底层的叶子名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值