Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。这里主要介绍的是基础类型
整形
Go语言同时提供了有符号和无符号类型的整数运算。这里有int8、int16、int32和int64四种截然不同大小的有符号整数类型,分别对应8、16、32、64bit大小的有符号整数,与此对应的是uint8、uint16、uint32和uint64四种无符号整数类型。 (无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等)
这里还有两种一般对应特定CPU平台机器字大小的有符号和无符号整数int和uint;其中int是应用最广泛的数值类型。这两种类型都有同样的大小,32或64bit,但是我们不能对此做任何的假设;因为不同的编译器即使在相同的硬件平台上可能产生不同的大小。
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。同样byte也是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
(int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是不同的类型,即使int的大小也是32bit,在需要将int当作int32类型的地方需要一个显式的类型转换操作,反之亦然。)
运算优先级
在Go语言中,%取模运算符的符号和被取模数的符号总是一致的,因此-5%3和-5%-3结果都是-2。除法运算符/的行为则依赖于操作数是否全为整数,比如5.0/4.0的结果是1.25,但是5/4的结果是1,因为整数除法会向着0方向截断余数。
优先级递减的顺序排列
取模运算符%仅用于整数间的运算
算术和逻辑运算的二元操作中必须是相同的类型 所以不同整数类型不能进行运算,如int8和int16 可以把他们都强转为int类型
对于将一个大尺寸的整数类型转为一个小尺寸的整数类型,或者是将一个浮点数转为整数,可能会改变数值或丢失精度:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
位运算
& 位运算 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}
八进制十六进制
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x) // 主要看一下输出格式 这里的x应该指的是十六进制 # 代表输出代表符号如0x和0分别表示十六进制和八进制
// Output:
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
ascii := 'a'
unicode := '国'
newline := '\n'
fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"
fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 国 '国'"
fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
// 字符使用%c参数打印,或者是用%q参数打印带单引号的字符
值域
值域其中有符号整数采用2的补码形式表示,也就是最高bit位用来表示符号位,一个n-bit的有符号数的值域是从-2n-1到2n-1-1。无符号整数的所有bit位都用于表示非负数,值域是0到2n-1
运算溢出,如果需要更多的bit位才能正确表示的话,就说明计算结果是溢出了。超出的高位的bit位部分将被丢弃。如果原始的数值是有符号类型,而且最左边的bit位是1的话,那么最终结果可能是负的,例如int8的例子:
浮点数
float32和float64:常量math.MaxFloat32表示float32能表示的最大数值,大约是 3.4e38;对应的math.MaxFloat64常量大约是1.8e308。它们分别能表示的最小值近似为1.4e-45和4.9e-324。
一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型,因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大(译注:因为float32的有效bit位只有23个,其它的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32的表示将出现误差):
var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1) // "true"!
math包中除了提供大量常用的数学函数外,还提供了IEEE754浮点数标准中定义的特殊值的创建和测试:正无穷大和负无穷大,分别用于表示太大溢出的数字和除零的结果;还有NaN非数,一般用于表示无效的除法操作结果0/0或Sqrt(-1).
var z float64
fmt.Println(z, -z, 1/z, -1/z, z/z) // "0 -0 +Inf -Inf NaN"
// 函数math.IsNaN用于测试一个数是否是非数NaN,math.NaN则返回非数对应的值,NaN和任何数都是不相等的
复数
Go语言提供了两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度。内置的complex函数用于构建复数,内建的real和imag函数分别返回复数的实部和虚部
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"
// 可以简化为
x := 1 + 2i
y := 3 + 4i
// 复数也可以用==和!=进行相等比较。只有两个复数的实部和虚部都相等的时候它们才是相等的
布尔型
true和false
字符串
一个字符串是一个不可改变的字节序列。字符串可以包含任意的数据,包括byte值0,但是通常是用来包含人类可读的文本。
有类似Python的切片功能
字符串可以用==和<进行比较;比较通过逐个字节比较完成的
s := "left foot"
t := s
s += ", right foot"
// 这并不会导致原始的字符串值被改变,但是变量s将因为+=语句持有一个新的字符串值,但是t依然是包含原先的字符串值。
s[0] = 'L' // compile error: cannot assign to s[0] 字符串不能直接被修改,不变性意味着如果两个字符串共享相同的底层数据的话也是安全的
字符串面值
字符串面值:双引号"",有转义操作
原生字符串面值:反引号``,便于正则表达式,没有转义操作(译注:在原生字符串面值内部是无法直接写字符的,可以用八进制或十六进制转义或+""连接字符串常量完成)
const GoUsage = `Go is a tool for managing Go source code.
Usage:
go command [arguments]
...`
转义字符
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
\' 单引号(只用在 '\'' 形式的rune符号面值中)
\" 双引号(只用在 "..." 形式的字符串面值中)
\\ 反斜杠
\xhh 十六进制 其中两个h表示十六进制数字(大写或小写都可以)
\ooo 八进制 包含三个八进制的o数字(0到7),但是不能超过\377(译注:对应一个字节的范围,十进制为255)
字符串通过下标索引的是byte,而不是对应的字符,这就证明了字符串本质是只读的字节数组;
func main() {
const placeOfInterest = `⌘`
fmt.Printf("plain string: ")
fmt.Printf("%s", placeOfInterest)
fmt.Printf("\n")
fmt.Printf("quoted string: ")
fmt.Printf("%+q", placeOfInterest)
fmt.Printf("\n")
fmt.Printf("hex bytes: ")
for i := 0; i < len(placeOfInterest); i++ {
fmt.Printf("%x ", placeOfInterest[i])
}
fmt.Printf("\n")
// 输出
// plain string: ⌘
// quoted string: "\u2318"
// hex bytes: e2 8c 98
}
编码
**ASCII:**包含英文字母的大小写、数字、各种标点符号和设备控制符。
**Unicode:**除了一些基本的还包含其他语言 如汉字等,每个Unicode码点都使用同样大小的32bit来表示。这种方式比较简单统一,但是它会浪费很多存储空间,因为大多数计算机可读的文本是ASCII字符,本来每个ASCII字符只需要8bit或1字节就能表示。
**UTF8:**将Unicode码点编码为字节序列的变长编码。UTF8编码使用1到4个字节来表示每个Unicode码点,ASCII部分字符只使用1个字节,常用字符部分使用2或3个字节表示。
Go语言的源文件采用UTF8编码。unicode包提供了诸多处理rune字符相关功能的函数(比如区分字母和数字,或者是字母的大写和小写转换等),unicode/utf8包则提供了用于rune字符序列的UTF8编码和解码的功能。
对于小于256的码点值可以写在一个十六进制转义字节中,例如\x41对应字符'A',但是对于更大的码点则必须使用\u或\U转义形式。
import "unicode/utf8"
s := "Hello, 世界"
fmt.Println(len(s)) // "13" 字节 一个汉字三个字节
fmt.Println(utf8.RuneCountInString(s)) // "9" 字符 直接数个数
UTF-8解码器
import (
"unicode/utf8"
)
for i := 0; i < len(s); {
// 返回一个r和长度,r对应字符本身,长度对应r采用UTF8编码后的编码字节数目
r, size := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%d\t%c\n", i, r)
i += size
}
打印
%x 十六进制
%q unicode方式输出
%s 字符串
https://blog.csdn.net/qq_18271763/article/details/127106020
func main() {
const placeOfInterest = `⌘`
fmt.Printf("plain string: ")
fmt.Printf("%s", placeOfInterest)
fmt.Printf("\n")
fmt.Printf("quoted string: ")
fmt.Printf("%+q", placeOfInterest)
fmt.Printf("\n")
fmt.Printf("hex bytes: ")
for i := 0; i < len(placeOfInterest); i++ {
fmt.Printf("%x ", placeOfInterest[i])
}
fmt.Printf("\n")
// 输出
// plain string: ⌘
// quoted string: "\u2318"
// hex bytes: e2 8c 98
// 在go语言中,int32的别名rune替代代码点,因此go中int32类型即可表示uncode字符集的字符数值;'⌘'为rune类型,十六进制为0x2318;
// Go 不保证字符串中的字符被规范化,即拥有不符合unicode、ASCII的无效任意转义字符。
}
字符串相关包
标准库中有四个包对字符串处理尤为重要:bytes、strings、strconv和unicode包。
strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。
//strigns
func Contains(s, substr string) bool
func Count(s, sep string) int
func Fields(s string) []string
func HasPrefix(s, prefix string) bool
func Index(s, sep string) int
func Join(a []string, sep string) string
bytes包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer类型将会更有效,稍后我们将展示。
// bytes
func Contains(b, subslice []byte) bool
func Count(s, sep []byte) int
func Fields(s []byte) [][]byte
func HasPrefix(s, prefix []byte) bool
func Index(s, sep []byte) int
func Join(s [][]byte, sep []byte) []byte
bytes包还提供了Buffer类型用于字节slice的缓存。一个Buffer开始是空的,但是随着string、byte或[]byte等类型数据的写入可以动态增长,一个bytes.Buffer变量并不需要初始化,因为零值也是有效的
// intsToString is like fmt.Sprint(values) but adds commas.
func intsToString(values []int) string {
var buf bytes.Buffer
buf.WriteByte('[')
for i, v := range values {
if i > 0 {
buf.WriteString(", ")
}
fmt.Fprintf(&buf, "%d", v)
}
buf.WriteByte(']')
return buf.String()
}
func main() {
fmt.Println(intsToString([]int{1, 2, 3})) // "[1, 2, 3]"
}
字符串和字节slice之间可以相互转换:
s := "abc"
b := []byte(s)
s2 := string(b)
// 将一个字节slice转换到字符串的string(b)操作则是构造一个字符串拷贝,以确保s2字符串是只读的。
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"
- 将一个字符串解析为整数
- 使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数
x, err := strconv.Atoi("123") // x is an int
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits ParseInt函数的第三个参数是用于指定整型数的大小;例如16表示int16,0则表示int
unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,它们用于给字符分类。每个函数有一个单一的rune类型的参数,然后返回一个布尔值。而像ToUpper和ToLower之类的转换函数将用于rune字符的大小写转换。所有的这些函数都是遵循Unicode标准定义的字母、数字等分类规范。strings包也有类似的函数,它们是ToUpper和ToLower,将原始字符串的每个字符都做相应的转换,然后返回新的字符串。
常量
常量表达式的值在编译期计算,而不是在运行期。每种常量的潜在类型都是基础类型:boolean、string或数字。
声明多个常量的时候需要用括号
// 常量就算没有标明类型也会自己推断的 但是不需要用:= 直接用=就行
const pi = 3.14159 // approximately; math.Pi is a better approximation
const (
e = 2.71828182845904523536028747135266249775724709369995957496696763
pi = 3.14159265358979323846264338327950288419716939937510582097494459
)
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量
iota 常量生成器
常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一。
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday
Thursday
Friday
Saturday
)
type Flags uint
const (
FlagUp Flags = 1 << iota // is up 1
FlagBroadcast // supports broadcast access capability 2
FlagLoopback // is a loopback interface 4
FlagPointToPoint // belongs to a point-to-point link 8
FlagMulticast // supports multicast access capability 16
)
无类型常量
许多常量并没有一个明确的基础类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。
对于常量面值,不同的写法可能会对应不同的类型。例如0、0.0、0i和\u0000虽然有着相同的常量值,但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。同样,true和false也是无类型的布尔类型,字符串面值常量是无类型的字符串类型。