GO学习笔记二-基础数据类型

706 阅读7分钟

1、关于2的补码

正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反[每一位取反(除符号位)]。

正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。

为什么负数的计算方法就是先取反码,再取补码(+1)#即 正数取反再+1# ?

8位计算机为例,+8就是0000 1000-8是怎么表示呢,最简单的理解是 0 - 80 我们可以用 0000 0000 表示,不够减需要向上借一位 1 0000 0000,这边0变成了9位了,为了计算方便,我们将1 0000 0000 拆成 1111 1111 + 1

-1在计算机里用二进制表达就是全1。16进制为:0xFFFFFFFF

-1 => +1 => 0000 0000 0000 0001 => 1111 1111 1111 1110 + 1=> 0xFFFF

8位-128为什么表示为 1 0000 0000

-128 => +128 => 1000 0000 => 0111 1111 + 1 => 1000 0000

2、bit 和 byte

1 byte = 8bit,如果是带符号位则值域为-2^(n-1)2^(n-1) - 1

fmt.Printf("int8'values range %d and %d\n", -1 << 7, 1 << 7 - 1)
fmt.Printf("int8'values range %d and %d\n", math.MinInt8, math.MaxInt8)
//output: int8'values range -128 and 127

3、整型

  1. 有符号的int8, int16, int32, int64, int
  2. 无符号的unit8, uint16, uint32, uint64, uint
  3. Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点
  4. byteuint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数
  5. %取模运算符的符号和被取模数的符号总是一致的,且仅用于整数间的运算,因此-5%3和-5%-3结果都是-2
  6. 除法运算符/的行为则依赖于操作数是否为全为整数,比如5.0/4.0的结果是1.25,但是5/4的结果是1,因为整数除法会向着0方向截断余数
  7. 大部分场景使用有符号数,无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等
  8. 算术运算符 +-*/ 整型相除还是得到整型,% 取余(模)
  9. bit位操作运算符 | or,& and, ^ xor 异或, &^ 按位消除, <<, >> 具体见下面的例子
  10. 取值范围可以用math包来获取,如int8范围是math.MinInt8math.MaxInt8
  11. 所占字节(1byte=4bit)长度可以用unsafe.Sizeof获取
const MaxUint = ^uint(0) 
const MinUint = 0 
const MaxInt = int(MaxUint >> 1) 
const MinInt = -MaxInt - 1

//int8'values range -128 and 127
fmt.Printf("int8'values range %d and %d\n", math.MinInt8, math.MaxInt8)

fmt.Printf("current os' int8 size is %d\n", unsafe.Sizeof(int8(1)))
fmt.Printf("current os' int16 size is %d\n", unsafe.Sizeof(int16(1)))
fmt.Printf("current os' int32 size is %d\n", unsafe.Sizeof(int32(1)))
fmt.Printf("current os' int64 size is %d\n", unsafe.Sizeof(int64(1)))
fmt.Printf("current os' int size is %d\n", unsafe.Sizeof(int(1)))
//output
//current os' int8 size is 1
//current os' int16 size is 2
//current os' int32 size is 4
//current os' int64 size is 8
//current os' int size is 8
package main

import (
	"fmt"
	"strconv"
)

func main() {
	var x uint8 = 1<<1 | 1<<2 | 1<<3 | 1<<5
	var y uint8 = 1<<1 | 1<<2 | 1<<4
	fmt.Printf("x   二进制:%08b, %s\n", x, getNumberSet(int(x), 8))//00101110 set{1, 2, 3, 5}
	fmt.Printf("y   二进制:%08b, %s\n", y, getNumberSet(int(y), 8))//00010110 set{1, 2, 4}
	fmt.Printf("x|y 二进制:%08b\n", x|y)//或运算 00111110 set{1, 2, 3, 4, 5} 并集
	fmt.Printf("x&y 二进制:%08b\n", x&y)//与运算 00000110 set{1, 2} 交集
	fmt.Printf("x^y 二进制:%08b\n", x^y)//异或运算 00111000 set{3,4,5} 对称差集
	fmt.Printf("x&^y二进制:%08b\n", x&^y)//按位消除 00101000 set{3, 5} 集合x相对于集合y的差集

	fmt.Printf("x<<1二进制:%08b\n", x<<1)//01011100 set{2,3,4,6}
	fmt.Printf("x>>1二进制:%08b\n", x>>1)//00010111 set{0,1,2,4}
}

//获取集合
func getNumberSet(num, bit int) string {
	var str = "set{"
	for i := 0; i < bit; i++ {
		if num & (1<<i) != 0 {
			str += strconv.Itoa(i) + ", "
		}
	}
	return str + "}"
}
ascii := 'a'
unicode := '国'
newline := '\n'
fmt.Printf("%d %[1]c %[1]q %[1]T\n", ascii)   // "97 a 'a'" int32
fmt.Printf("%d %[1]c %[1]q %[1]T\n", unicode) // "22269 国 '国'" int32
fmt.Printf("%d %[1]q %[1]T\n", newline)       // "10 '\n'" int32

4、浮点数

  1. Go语言提供了两种精度的浮点数,float32float64
  2. 最大范围可以用math包获取 eg. math.MaxFloat64 math.MaxFloat32
  3. 所占字节(1byte=4bit)长度可以用unsafe.Sizeof获取
  4. 一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型,因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大(因为float32的有效bit位只有23个,其它的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32的表示将出现误差)
  5. 小数点前面或后面的数字都可能被省略(例如.707或1.)
//float32 byte size: 4, max value : 3.402823E+38
fmt.Printf("float32 byte size: %d, max value : %E\n", unsafe.Sizeof(float32(1)), math.MaxFloat32)

//float32 超过23bit就有问题
var f1, f2 float32 = 1<<23, 1<<24
fmt.Printf("f1:%[1]T f1=%[1]f,f1+1=%[2]f,f1==f1+1? %[3]t\n", f1, f1+1, f1==f1+1)
fmt.Printf("f2:%[1]T f2=%[1]f,f2+1=%[2]f,f2==f2+1? %[3]t\n", f2, f2+1, f2==f2+1)
//output
//f1:float32 f1=8388608.000000,f1+1=8388609.000000,f1==f1+1? false
//f2:float32 f2=16777216.000000,f2+1=16777216.000000,f2==f2+1? true

//小数点前面或后面的数字都可能被省略(例如.707或1.)
var f3, f4 = .19, 8.
fmt.Printf("f3: %f, %[1]T\n", f3)
fmt.Printf("f4: %f, %[1]T\n", f4)
//f3: 0.190000, float64
//f4: 8.000000, float64

5、布尔值

  1. &&对应逻辑乘法,||对应逻辑加法,乘法比加法优先级要高,&&的优先级比||高
  2. 不像php,go语言中布尔值并不会隐式转换为数字值0或1,反之亦然
  3. 不能用bool(int)int转为bool类型,会报cannot convert xx (type int) to type bool 反之亦然: go中定义true0 == 0, false0 != 0
//go 中 true 和 false 的定义
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)

//err:cannot convert 1 (type untyped int) to type bool
b1 := bool(1)

var i1 = 1
//err:non-bool i1 (type int) used as if condition
if i1 {
	fmt.Println("Todo sth!")
}

6、字符串

  1. 字符串是一个不可改变的字节序列:与php不同的地方,字符串是不能修改的
  2. 内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目),索引操作s[i]返回第i个字节的字节值
  3. 字符串转为字节数组,如果都是英文,可以用[]byte(s1),如果有中文则应该用[]rune(s1)byteuint8的别名,runeint32的别名
  4. 字符串底层就是Byte数组
  5. 只是针对于英文情况下一个英文对应一个byte[uint8 1字节]但是中文不一定,goutf-8形式编码,而中文在utf-8是占三个字节,如果以byte类型存中文显然会溢出,所以为了解决上述情况,gorune[int32 4字节]类型存
  6. []byte方法和[]rune方法是将字符串转化成对应类型的slice(slice是引用类型,引用数组),string方法是转化为字符串
//字符串不能修改
//err:cannot assign to s1[1]
var s1 = "hello loco"
s1[1] = 'x';
var s1 = "I love you,中国!"
var b1 = []byte(s1)
var r1 = []rune(s1)
//b1: []byte{0x49, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x2c, 0xe4, 0xb8, 0xad, 0xe5, 0x9b, 0xbd, 0xef, 0xbc, 0x81}, []uint8(20)
//r1: []int32{73, 32, 108, 111, 118, 101, 32, 121, 111, 117, 44, 20013, 22269, 65281}, []int32(14)
fmt.Printf("b1: %#[1]v, %[1]T(%[2]d)\n", b1, len(b1))
fmt.Printf("r1: %#[1]v, %[1]T(%[2]d)\n", r1, len(r1))

for k, v := range b1{
	fmt.Printf("%03[1]d : %05[2]d\t%[2]c\t%[3]s\n", k, v, string(v))
}

for k, v := range r1{
	fmt.Printf("%03[1]d : %05[2]d\t%[2]c\t%[3]s\n", k, v, string(v))
}