A quick look at what are the basic data types in go

integer

Divided by bit length: 8 bits, 16 bits, 32 bits, 64 bits.

Corresponds to signed integers: int8, int16, int32, int64.

Corresponds to unsigned integers: uint8, uint16, uint32, uint64.

Rules for calculating maximum and minimum numbers:

  • Signed integer: Because of the sign, the highest bit of the number of bits is used to store the sign, and the others are used to store the data value, so for the number of nbits, the value range is: -2^(n-1)^~2^ (n-1)^-1, which corresponds to int8, is -127 ~ 127.
  • Unsigned positive number: No need to record the sign with the highest bit, all digits represent the number. The value range is: 0 ~ 2^n^-1. For int8, it is 0 ~ 255.

special integer types

intand uintas well uintptr.

intAnd uintdepending on the specific platform, its size is the same as the native integer or the most computationally efficient value on that platform. Either 64-bit or 32-bit at the same time, but neither can be assumed to be 64-bit or 32-bit integers. Even under the same hardware, different compilers may choose different sizes.

As for uintptrits size, it's not clear. But it's definitely big enough to hold a pointer. Often used for low-level programming.

Although int, uintmay have the same length as int32, int64, and ..other integer types, they are different types.

If you want to determine the size of the intsumuint

fmt.Println(runtime.GOARCH)		// 看看CPU型号
fmt.Println(strconv.IntSize)	// 这才是int的大小,上面只是提供一个对照

overflow

Whether it is signed or unsigned, if the number of bits required to calculate the result exceeds the range of the type, it is called overflow . The overflowed high bits are silently discarded.

var i int8 = -128
fmt.Println(i-1)	// 127
var i2 int8 = 127
fmt.Println(i2+1)	// -128
var u uint8 = 0
fmt.Println(u-1)	// 255
var u2 uint8 = 255
fmt.Println(u2+1)	// 0

floating point number

float32, float64. Follow the IEEE 754standard.

math.MaxFloat32float32The maximum value of the type is given : 3.4028234663852886e+38.

math.MaxFloat64则是float64类型的最大值:1.7976931348623157e+308

浮点数可以用来表示小数。

十进制下,float32有效位大约是6位。float64有效位大约是15位。

浮点数的打印可以使用:

  1. %g,保留足够的精度显示
  2. %e,使用指数形式显示
  3. %f,使用无指数形式显示
f := 2.71828
fmt.Printf("%g, %[1]e, %[1]f \n", f)	// 2.71828, 2.718280e+00, 2.718280

复数

complex64complex128 ,二者分别由float32float64组成。

使用real函数提取复数的实部,使用imag函数提取复数的虚部。

浮点数或者整数后面加i就会变成一个虚数,且它的实部为0

x := 1+2i
y := 3+4i

布尔值

bool类型就是布尔值,有true(真)和false(假)两个值。

布尔值无法隐式转换成数值,数值也不能转成布尔值。

它的零值是false

一元操作符!表示逻辑取反。!true表示false

字符串

string表示字符串。它是不可变的字节序列。Go中的字符串内部实现用UTF-8编码。

字符串的“长度”与遍历字符串的做法

字符串的“长度”

对字符串调用len函数,获取到的不是字符串的长度(字符的个数),而是字符串的字节数(字节切片的长度)。

如下,尽管字符个数只有6,但字节长度len(s)却有18,这是因为中文字符以UTF-8存储,通常包含3~4个字节。

s := "中文字节数多"
fmt.Println("len: ", len(s))	// len:  18

对字符串使用下标索引操作,会返回对应索引的字节,而不是字符。

s := "中文字节数多"
fmt.Println("len: ", len(s))	// len:  18
for i :=0; i < len(s); i++ {
    fmt.Printf("%d, %[1]c, %[1]T\n",s[i])
}

对应的输出如下:

len: 18
228, ä, uint8
184, ¸, uint8
173, ­, uint8
230, æ, uint8
150, �, uint8
135, �, uint8
229, å, uint8
173, ­, uint8
151, �, uint8
232, è, uint8
138, �, uint8
130, �, uint8
230, æ, uint8
149, �, uint8
176, °, uint8
229, å, uint8
164, ¤, uint8
154, �, uint8

因此不要随便乱用字符串的下标操作,否则可能获得有意想不到的结果。

遍历字符串

由上面的下标操作可以看出,对字符串的下标索引操作会获得单个字节而不是字符,假如现在我们想处理的是UTF-8解码的字符的话,有两种方式,基本思路都是处理成rune类型:

第一种,用UTF-8解码器显式处理这些字符,unicode/utf8包示例。

utf8.RuneCountInString(s)返回字符串转为rune后的个数,Go使用rune代表一个UTF-8字符。

utf8.utf8.DecodeRuneInString(s[i:])处理当前字符串,并算出下一个rune以及它所占的字节数。

s := "What? 中文字节数多"
runeCount := utf8.RuneCountInString(s)
fmt.Println("runeCount:", runeCount)	// runeCount: 12
for i:= 0; i<len(s); {
	r, size:= utf8.DecodeRuneInString(s[i:])
	fmt.Printf("i: %d, r:%q, type:%T \n", i, r, r)
	i += size
}

输出如下:

runeCount: 12
i: 0, r:'W', type:int32
i: 1, r:'h', type:int32
i: 2, r:'a', type:int32
i: 3, r:'t', type:int32
i: 4, r:'?', type:int32
i: 5, r:' ', type:int32
i: 6, r:'中', type:int32
i: 9, r:'文', type:int32
i: 12, r:'字', type:int32
i: 15, r:'节', type:int32
i: 18, r:'数', type:int32
i: 21, r:'多', type:int32

第二种,用range循环,Go会隐式的进行UTF-8解码。

注意,这里的i,指的是字节的下标,而不是字符的下标。

s := "What? 中文字节数多"
for i, r := range s {
    fmt.Printf("i: %d, rune: %q, type: %T \n", i, r, r)
}

输出如下:

i: 0, rune: 'W', type: int32
i: 1, rune: 'h', type: int32
i: 2, rune: 'a', type: int32
i: 3, rune: 't', type: int32
i: 4, rune: '?', type: int32
i: 5, rune: ' ', type: int32
i: 6, rune: '中', type: int32
i: 9, rune: '文', type: int32
i: 12, rune: '字', type: int32
i: 15, rune: '节', type: int32
i: 18, rune: '数', type: int32
i: 21, rune: '多', type: int32

Rune与Byte(字节)

int32的别名是rune,天然适合存储单个文字符号,为Go所采用的。

rune类型值代表一个UTF-8字符。以字节(byte)为单位对Unicode码点作变长编码。现在计算机都用UTF-8来表示单个字符。

字符串的是由“字符”组成的,字符用单引号包裹起来,如:

var b = 'h'
c := '冲'
fmt.Printf("%d, %q \n", b, b)	// 104, 'h'
fmt.Printf("%d, %q \n", c, c)	// 20914, '冲'

字节,byte类型,底层类型是uint8,由8个bit组成,它可以代表一个ASCII码。ASCII码是满足早期计算机的使用的,它用7位表示128个“字符”(ASCII字符):大小写英文字母、数字、标点符号和设备控制符。

字符串与字节slice的转换

字符串底层是一个字节数组,所以可以和[]byte类型互换。

s := "abc"
b := []byte(s)
s2 := string(b)

概念上,[]byte(s)转换操作会分配新的字节数组,拷贝填入s含有的字节,并生成一个slice的引用,指向整个数组。反之,用string(b)也会产生一份副本而不是操作真正的b,以此保证上面s2不变。

字符串不可变

字符串是不可变的,表现在其字符串值不可修改。平时我们看到的字符串修改操作、拼接操作并不改变原有的字符串值,而是将操作后生成新的字符串值赋予原来的变量。

s := "left foot"
t := s
s += ", right foot"

尽管字符串创建后,它底层的字节slice不可变,但是普通的字节slice是可以随意改变的。

var a = []byte{'h', 'e', 'l', 'l', 'o'}
fmt.Printf("%p, %[1]q \n", a)	// 0xc00000a098, "hello"
a[4] = ' '
fmt.Printf("%p, %[1]q \n", a)	// 0xc00000a098, "hell "

由于字符串的不可变以及避免频繁的操作字符串而导致的多次内存分配和复制,可以使用bytes.Buffer类型。

见GOPL的一个例子:

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]
}

追加ASCII字符可以用writeByte,追加UTF-8编码的文字符号,最好用WriteRune方法。

基本类型的值都是可比较的

基本类型的值都是可比较的,如布尔值、数值、字符串等。

数值的类型转换

很多整型—整型的转换不会引起值的变化,仅告知编译器如何解读这么值。但缩减大小的类型转换,以及整型与浮点型的相互转换,会因此值的改变或者损失精度。

浮点型转整型会舍弃小数部分并向0取整。

var f = 3.526
i := int(f)
fmt.Printf("f: %v, i: %v \n", f, i) // f: 3.526, i: 3 
var i16 = int16(555)
var i8 = int8(i16)
fmt.Printf("i16: %v, i8: %v \n", i16, i8) // i16: 555, i8: 43 

运算符

运算符降序排列:

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

二元运算符分为五大优先级。同级别的运算符满足左结合律,可以用圆括号指定次序。

常量

常量是一种表达式,保证在编译阶段就计算出对应的值。所有常量本质上都属于基本类型:布尔型、字符串或者数字。

常量自编译后,其值恒定不变。

type Integer int

const I Integer = 10

const S string = "important_secret"

const (
	NUM1 = 1
	NUM2 = 2
	NUM3
	NUM4 = 5.5
	STR1 = "STR1"
)

func main() {
	fmt.Printf("I: %v \n", I)
	fmt.Printf("S: %v \n", S)
	fmt.Printf("NUM1: %v \n", NUM1)
	fmt.Printf("NUM2: %v \n", NUM2)
	fmt.Printf("NUM3: %v \n", NUM3)
	fmt.Printf("NUM4: %v \n", NUM4)
	fmt.Printf("STR1: %v \n", STR1)
}

输出如下:

I: 10
S: important_secret
NUM1: 1
NUM2: 2
NUM3: 2
NUM4: 5.5
STR1: STR1

本文正在参加技术专题18期-聊聊Go语言框架

Guess you like

Origin juejin.im/post/7120939408051994638