所有的数据都是由比特组成
整型
- Go语言的数值类型包括几种不同大小的整数、浮点数和复数。
- 每种数值类型都决定了对应的大小范围和是否支持正负符号。
- Go语言同时提供了有符号和无符号类型的整数运算。
四种不同大小的有符号整数类型int8
、int16
、int32
和int64
,分别对应8、16、32、64bit大小,与此对应的是uint8
、uint16
、uint32
和uint64
四种无符号整数类型。
✋还有两种一般对应特定CPU平台机器字大小的有符号和无符号整数int
和uint
,其中int
是应用最广泛的数值类型。
- Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。
- byte是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
- 无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。
❌ int
和int32
也是不同的类型,即使int的大小也是32bit。
- 有符号整数采用2的补码形式表示,最高bit位用来表示符号位,一个n-bit的有符号数的值域是从
-2^(n-1)
到2^(n-1)-1
。 - 无符号整数的所有bit位都用于表示非负数,值域是
0
到2^n-1
。
eg: int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
算术运算、逻辑运算和比较运算的二元运算符,按照优先级递减的顺序排列:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
二元运算符有五种优先级。在同一个优先级,使用左优先结合规则,但是使用括号可以明确优先顺序,使用括号也可以用于提升优先级,例如mask & (1 << 28)
。
⛲️逻辑表达式的结果是布尔类型。
== 等于
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
⌛️ 一元的加法和减法运算符
+ 一元加法(无效果)
- 负数
2️⃣bit
位操作运算符
前面4个操作运算符不区分是有符号还是无符号数
& 位运算 AND
| 位运算 OR
^ 位运算 XOR
&^ 位清空(AND NOT)
<< 左移
>> 右移
位操作运算符^
作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反,位操作运算符&^
用于按位置零(AND NOT)
对于每种类型T,如果转换允许的话,类型转换操作T(x)
将x
转换为T
类型
许多整数之间的相互转换并不会改变数值;它们只是告诉编译器如何解释这个值。
对于将一个大尺寸的整数类型转为一个小尺寸的整数类型,或者是将一个浮点数转为整数,可能会改变数值或丢失精度
f := 3.141 // a float64
i := int(f)
fmt.Println(f, i) // "3.141 3"
f = 1.99
fmt.Println(int(f)) // "1"
整数字面值都可以用以0
开始的八进制格式书写, 或用以0x
或0X
开头的十六进制格式书写。
ioutil.WriteFile("fileName", []byte("xxx"), 0777)//rwx
浮点数
- Go语言提供了两种精度的浮点数,
float32
和float64
。
它们的算术规范由IEEE754浮点数国际标准定义。 - 浮点数的范围极限值可以在math包找到。
eg 常量math.MaxFloat32
表示float32
能表示的最大数值。
小数点前面或后面的数字都可能被省略(.707或1.)。也可以用科学计数法书写,通过e
或E
来指定指数部分
const e = 10e2//1000
复数
- 两种精度的复数类型:
complex64
和complex128
,分别对应float32
和float64
两种浮点数精度。 - 内置的
complex
函数用于构建复数,real
和imag
函数分别返回复数的实部和虚部:
//(a+bi)(c+di)=(ac-bd)+(bc+ad)i
var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x * y) // "(-5+10i)"
fmt.Println(real(x * y)) // "-5"
fmt.Println(imag(x * y)) // "10"
一个浮点数面值或一个十进制整数面值后面跟着一个i,(3.141592i或2i),它将构成一个复数的虚部,复数的实部是0:
fmt.Println(1i * 1i) // "(-1+0i)", i^2 = -1
布尔
- 布尔类型的值只有两种:
true
和false
。 - 逻辑运算符会产生布尔型的值。
- 一元操作符
!
对应逻辑非操作,!true
的值为false
。 - 布尔值和
&&
和||
操作符结合可能会短路。 - 布尔值并不会隐式转换为数字值0或1,反之亦然。
字符串
- 一个字符串是一个不可改变的字节序列。
- 字符串可以包含任意的数据,通常是用来包含人类可读的文本。
- 文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。
- 内置的
len
函数可以返回一个字符串中的字节数目不是rune
字符数目。 - 索引操作
s[i]
返回第i个字节的字节值,i
必须满足0 ≤ i< len(s)
条件约束。
s := "hello, world"
fmt.Println(len(s)) // "12"
fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w')
访问超出字符串索引范围的字节将会导致panic
异常:
c := s[len(s)] // panic: index out of range
第i
个字节并不一定是字符串的第i个字符,对于非ASCII
字符的UTF8
编码会要两个或多个字节。
子字符串操作s[i:j]
基于原始的s
字符串的第i
个字节开始到第j
个字节不包含j
生成一个新字符串。生成的新字符串将包含j-i
个字节。
fmt.Println(s[0:5]) // "hello"
i
或者j
都可能被忽略,当它们被忽略时将采用0
作为开始位置,采用len(s)
作为结束的位置。
fmt.Println(s[:5]) // "hello"
fmt.Println(s[7:]) // "world"
fmt.Println(s[:]) // "hello, world"
+
操作符将两个字符串连接构造一个新字符串
fmt.Println("goodbye" + s[5:]) // "goodbye, world"
字符串可以用==
和<
进行比较;比较通过逐个字节比较完成的,比较的结果是字符串自然编码的顺序。
字符串的值是❌不可变的。
func main() {
s := "left"
t := s
s += ", right"
fmt.Println(s, "<-->", t)
}
字符串是不可修改的,修改字符串内部数据的操作也被禁止
s[0] = 'L' // compile error: cannot assign to s[0]
✌不变性意味着如果两个字符串共享相同的底层数据的话也是安全的,复制任何长度的字符串代价是低廉的。
字符串面值
字符串直接写在双引号里面
"Hello, 世界"
转义
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
\' 单引号(只用在 '\'' 形式的rune符号面值中)
\" 双引号(只用在 "..." 形式的字符串面值中)
\\ 反斜杠
可以通过十六进制或八进制转义在字符串面值中包含任意的字节
- 一个十六进制的转义形式是
\xhh
,其中两个h表示十六进制数字(大写或小写都可以)。 - 一个八进制转义形式是
\ooo
,包含三个八进制的o数字(0到7),不能超过\377
❓ 。
一个原生的字符串面值形式是`...`,使用反引号代替双引号。
在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思。
func main() {
s := `
很多
很多\
行`
fmt.Println(s)
}
Unicode
在很久以前,世界还是比较简单的,起码计算机世界就只有一个ASCII字符集:美国信息交换标准代码。ASCI,使用7bit来表示128个字符:包含英文字母的大小写、数字、各种标点符号和设备控制符。随着互联网的发展,混合多种语言的数据变得很常见。如何有效处理这些包含了各种语言的丰富多样的文本数据呢❓
Unicode
http://unicode.org,它收集了这个世界上所有的符号系统,包括重音符号和其它变音符号,制表符和回车符,还有很多神秘的符号,每个符号都分配一个唯一的Unicode码点。
UTF-8
- UTF8是一个将Unicode码点编码为字节序列的变长编码。UTF8编码是由Go语言之父Ken Thompson和Rob Pike共同发明的。
- UTF8编码使用1到4个字节来表示每个Unicode码点,ASCII部分字符只使用1个字节,常用字符部分使用2或3个字节表示。
每个符号编码后第一个字节的高端bit位用于表示编码总共有多少个字节。如果第一个字节的高端bit为0,则表示对应7bit的ASCII字符,ASCII字符每个字符依然是一个字节,和传统的ASCII编码兼容。如果第一个字节的高端bit是110,则说明需要2个字节;后续的每个高端bit都以10开头。
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
字符串和Byte切片
字符串和Byte切片转换
s := "zxysilent"
b := []byte(s)
s1 := string(b)
fmt.Println(s, b, s1)
标准库中有四个包对字符串处理尤为重要:bytes
、strings
、strconv
和unicode
包。
strings
包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。bytes
包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte
类型。 因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer
类型将会更有效。strconv
包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。unicode
包提供了IsDigit
、IsUppe
r和IsLower
等类似功能,它们用于给字符分类。
字符串和数字的转换
除了字符串、字符、字节之间的转换,字符串和数值之间的转换也比较常见。
strconv
包提供这类转换功能。
将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa(“整数到ASCII”):
x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"
FormatInt
和FormatUint
函数可以用不同的进制来格式化数字
fmt.Println(strconv.FormatInt(int64(x), 2)) // "1111011"
fmt.Printf
函数的%b
、%d
、%o
和%x
等也可用来格式化:
s := fmt.Sprintf("x=%b", x) // "x=1111011"
将一个字符串解析为整数,可以使用strconv
包的Atoi
或ParseInt
,ParseUint
(无符号整数)函数
x, err := strconv.Atoi("123")
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits
ParseInt
函数的第三个参数是用于指定整型数的大小;eg16
表示int16
,0
则表示int
, 返回的结果总是int64
类型。
也可使用fmt.Scanf
来解析输入的字符串和数字。
fmt.Printf(format string, a ...interface{})
fmt.Fprintf(w io.Writer, format string, a ...interface{})
fmt.Sprintf(format string, a ...interface{})
fmt.Scanf(format string, a ...interface{})
fmt.Fscanf(r io.Reader, format string, a ...interface{})
fmt.Sscanf(str string, format string, a ...interface{})
常量
- 常量表达式的值在编译期计算,而不是在运行期。
- 每种常量的潜在类型都是基础类型。
const pi = 3.14159
批量声明多个常量。
const (
e = 2.718281
pi = 3.141592
)
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len
、cap
、real
、imag
、complex
和unsafe.Sizeof
。
const L=len("adcd")//4
const L = unsafe.Sizeof(int(1))//8
一个常量的声明也可以包含一个类型和一个值,但是如果没有显式指明类型,那么将从右边的表达式推断类型。通过%T参数打印类型信息
//type Duration int64
const noDelay time.Duration = 0
const timeout = 2 * time.Minute
fmt.Printf("%T %[1]v\n", noDelay)
fmt.Printf("%T %[1]v\n", timeout)
批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
iota 常量生成器
在一个const
声明语句中,在第一个声明的常量所在的行,iota
将会被置为0
,然后在每一个有常量声明的行加一。
const (
a = iota
b
c
)
const (
d = iota
e
f
)
fmt.Println(a, b, c, d, e, f)
Comments