这是我的第3篇笔记
基本类型
总
int
int8、int16、int32(也是int)和int64 对应着 1b,4b,5b,6b
8,16,32,64,分别对应1b,2b,4b,8b
字符
单个字符,会被编译器推断为int32类型
\
单个字符,可以用rune,int32,byte,uint8
同时,他们可以进行算法运算, 如果有其他int参与运算,那么得用相应的显示转换
\
一个n-bit的有符号数的值域是从-2^{n-1}到2^{n-1}-1。无符号整数的所有bit位都用于表示非负数,值域是0到。
例如,int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
Unicode字符rune类型是和int32等价的类型, (UTF8字符) 通常用于表示一个Unicode码点。这两个名称可以互换使用。
同样byte也是uint8类型的等价类型, (ASCII字符) byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
\
\
\
运算符
按照优先级递减的顺序排列
没有前加减,只有后加减,i++,i--,而且只能单条语句使用,不能当表达
\
&& 的优先级比 || 高, 2个都有有短路行为
\
位运算
前面4个操作运算符并不区分是有符号还是无符号数
位操作运算符 ^ 作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反
有符号数的右移运算会用符号位的值填充左边空缺的bit位,因此如果是负数,会一直填1
我们还是倾向于使用有符号的int类型,就像数组的长度那样,虽然使用uint无符号类型似乎是一个更合理的选择
无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合
位操作运算符 &^ 用于按位置零(AND NOT):
如果对应y中bit位为1的话,表达式z = x &^ y结果z的对应的bit位为0,否则z对应的bit位等于x相应的bit位的值
如 x= 111 y=101 x&^y=z 首先看y是1的,z为0,即0-0 再y是0的,对应x,即010
类型转换
不能进行隐私类型转换
只能进行显示类型转换-----var a int=4 var b int8 b = int8(a)
int8() 是类型转换操作,指针要这样 *(int)(p)
许多整数之间的相互转换并不会改变数值;它们只是告诉编译器如何解释这个值。但是对于将一个大尺寸的整数类型转为一个小尺寸的整数类型,或者是将一个浮点数转为整数,可能会改变数值或丢失精度:
没事
var apples int32 = 1 var oranges int16 = 2 var compote = int(apples) + int(oranges)
丢失精度
f := 3.141 // a float64 i := int(f)//i=3
进制
任何大小的整数字面值都可以用
- 以0开始的八进制格式书写,例如0666;
- 以0x或0X开头的十六进制格式书写,例如0xdeadbeef。
- 十六进制数字可以用大写或小写字母。
- 如今八进制数据通常用于POSIX操作系统上的文件访问权限标志,十六进制数字则更强调数字值的bit位模式
- 用fmt.Printf 和 %d、%o、%x 、%b格式化输出
fmt.Printf输出技巧
ten := 10
fmt.Printf("decimal:%d binay:%#[1]b octal:%#[1]o hexdecimal:%#[1]x,%#[1]X", ten)
decimal:10 binay:0b1010 octal:012 hexdecimal:0xa,0XA
[1] 副词 ,表示仍使用第1个参数(格式化字符串是第0个参数),故[2]表示第2个参数
# 副词 ,表示输出类型的格式
%q 能输出单个字符
fmt.Printf("%q", '哈')---'哈'
%T 输出类型 ,%v 输出类型的值 %t是布尔
如, fmt.Printf("%T %[1]v", 1)int 1
浮点数
一般使用float64
一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度
因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大(译注:因为float32的有效bit位只有23个,其它的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32的表示将出现误差
var f float32 = 16777216 // 1 << 24 fmt.Println(f == f+1) // "true"!
用Printf函数的 %g----少输出0打印浮点数,将采用更紧凑的表示形式打印,并提供足够的精度,但是对应表格的数据,
使用 %e----输出所有0(带指数)或 %f-----不采用科学计数法的形式打印可能更合适
fmt.Println(0, -0, 1/0, -1/0, 0/0) // "0 -0 +Inf -Inf NaN"
复数
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"
var a=3i+2 //这是一个复数,虚部是3,实部是2
\
布尔
并不会隐式转换为数字值0或1
字符串
特性
一个字符串是一个不可改变的字节序列
切片会产生新字符串
+会产生大量废弃字符串
string和java的一样,用+会产生大量废弃字符串,虽然会回收,但开销大,可以用strings.Join()把字符串数组连接起来
b := [...]string{"1", "2", "3", "4", "5"}
fmt.Println(strings.Join(b[0:], " "))
拼接字符串
strings.Builder>bytes.Buffer >+
\
- strings.Builder,bytes.Buffer 的内存是以倍数申请的
- strings.Builder 和 bytes.Buffer 底层都是 []byte 数组,bytes.Buffer 转化为字符串时重新申请了一块空间,存放生成的字符串变量,而 strings.Builder 直接将底层的 []byte 转换成了字符串类型返回
比较
字符串可以用==和<进行比较;比较通过逐个字节比较完成的,因此比较的结果是字符串自然编码的顺序
其他
因为Go语言源文件总是用UTF8编码,并且Go语言的文本字符串也以UTF8编码的方式处理,因此我们可以将Unicode码点也写到字符串面值中。
\
\
转义字符
可以用\n,也可以用十六进制\xn或者八进制\0n,但其十进制范围是0-255
原生的字符串
如,var a=`asd\n`所有都是字符,没有转义
在原生字符串面值内部是无法直接写字符的,可以用八进制或十六进制转义 或 +""连接字符串常量完成
unicode
Unicode码点对应Go语言中的rune整数类型(译注:rune是int32等价类型)
在第八版本的Unicode标准里收集了超过120,000个字符,涵盖超过100多种语言。这些在计算机程序和数据中是如何体现的呢?通用的表示一个Unicode码点的数据类型是int32,也就是Go语言中rune对应的类型;它的同义词rune符文正是这个意思。
我们可以将一个符文序列表示为一个int32序列。这种编码方式叫UTF-32或UCS-4,每个Unicode码点都使用同样大小的32bit来表示。这种方式比较简单统一,但是它会浪费很多存储空间,因为大多数计算机可读的文本是ASCII字符,本来每个ASCII字符只需要8bit或1字节就能表示。而且即使是常用的字符也远少于65,536个,也就是说用16bit编码方式就能表达常用字符。但是,还有其它更好的编码方法吗?
unicode包提供了诸多处理rune字符相关功能的函数(比如区分字母和数字,或者是字母的大写和小写转换等),unicode/utf8包则提供了用于rune字符序列的UTF8编码和解码的功能。
标准库中有四个包对字符串处理尤为重要
bytes、strings、strconv和unicode包
- strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。
- bytes包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer类型将会更有效,稍后我们将展示。
- strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。
- unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,它们用于给字符分类。每个函数有一个单一的rune类型的参数,然后返回一个布尔值。而像ToUpper和ToLower之类的转换函数将用于rune字符的大小写转换。所有的这些函数都是遵循Unicode标准定义的字母、数字等分类规范。strings包也有类似的函数,它们是ToUpper和ToLower,将原始字符串的每个字符都做相应的转换,然后返回新的字符串。
字符串与字节切片
一个字符串是包含只读字节的数组,一旦创建,是不可变的。相比之下,一个字节slice的元素则可以自由地修改。
字符串和字节slice之间可以相互转换:
s := "abc" b := []byte(s) s2 := string(b)修改切片b的元素并不会改变s,即切片b并没有引用s的字节数组
strings包中的六个函数:
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包中也对应的六个函数:
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
它们之间唯一的区别是字符串类型参数被替换成了字节slice类型的参数
bytes包还提供了Buffer类型用于字节slice的缓存。一个Buffer开始是空的,但是随着string、byte或[]byte等类型数据的写入可以动态增长,一个bytes.Buffer变量并不需要初始化,因为零值也是有效的:
如, var buf bytes.Buffer
当向bytes.Buffer添加任意字符的UTF8编码时,最好使用bytes.Buffer的WriteRune方法,但是WriteByte方法对于写入类似'['和']'等ASCII字符则会更加有效。
strconv包
x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"
\
strconv.FormatInt(int64(x), 2) 传入一个十进制,返回一个二进制
如果要将一个字符串解析为整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数
常量
常量表达式的值在编译期计算,而不是在运行期
因为它们的值是在编译期就确定的,因此常量可以是构成类型的一部分,例如用于指定数组类型的长度:var p [size]int
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof
自动赋值常量
如果省略初始化表达式则表示使用前面常量的初始化表达式写法
const ( a = 1 b c = 2 d ) fmt.Println(a, b, c, d) // "1 1 2 2"
iota 常量生成器
在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一
//整数
type Weekday int
const (
//从这行开始,从0累加,都是Weekday类型---------类似枚举
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
//位运算
type Flags uint
const (
FlagUp Flags = 1 << iota // is up
FlagBroadcast // supports broadcast access capability
FlagLoopback // is a loopback interface
FlagPointToPoint // belongs to a point-to-point link
FlagMulticast // supports multicast access capability
)
//每个常量都是1024的幂,但它并不能用于产生1000的幂(KB、MB等)
const (
_ = 1 << (10 * iota)
KiB // 1024
MiB // 1048576
GiB // 1073741824
TiB // 1099511627776 (exceeds 1 << 32)
PiB // 1125899906842624
EiB // 1152921504606846976
ZiB // 1180591620717411303424 (exceeds 1 << 64)
YiB // 1208925819614629174706176
)
无类型常量
编译器为这些没有明确基础类型的数字常量提供比基础类型更高精度的算术运算;你可以认为至少有256bit的运算精度。这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。
对于常量面值,不同的写法可能会对应不同的无类型
0、0.0、0i和\u0000 分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符
//无类型的常量将会被隐式转换为对应的类型
var f float64 = 3 + 0i // untyped complex -> float64
f = 2 // untyped integer -> float64
f = 1e123 // untyped floating-point -> float64
f = 'a' // untyped rune -> float64
//对于一个没有显式类型的变量声明(包括简短变量声明),常量的形式将隐式决定变量的默认类型
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)