goの基本的なデータ型を簡単に確認する

整数

ビット長で割った値:8ビット、16ビット、32ビット、64ビット。

符号付き整数に対応します:int8、int16、int32、int64。

符号なし整数に対応します:uint8、uint16、uint32、uint64。

最大数と最小数を計算するためのルール:

  • 符号付き整数:符号があるため、ビット数の最上位ビットが符号の格納に使用され、その他のビットはデータ値の格納に使用されます。したがって、nビット数の値の範囲は次のとおりです。-2 ^( n-1)^〜2 ^(n-1)^-1はint8に対応し、-127〜127です。
  • 符号なしの正の数:最上位桁の符号を記録する必要はありません。すべての桁が数値を表します。値の範囲は0〜2 ^ n^-1です。int8の場合、0〜255です。

特殊整数型

intそしてuint同様にuintptr

intまたuint、特定のプラットフォームに応じて、そのサイズはネイティブ整数またはそのプラットフォームで最も計算効率の高い値と同じになります。同時に64ビットまたは32ビットのいずれかですが、どちらも64ビットまたは32ビットの整数であると見なすことはできません。同じハードウェアの下でも、コンパイラが異なればサイズも異なります。

サイズはuintptr不明です。しかし、それは間違いなくポインタを保持するのに十分な大きさです。多くの場合、低レベルのプログラミングに使用されます。

int、は、、、およびその他の整数型とuint同じ長さint32である可能性がありますが、それらは異なる型です。int64..

int合計uintのサイズを決定したい場合

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

オーバーフロー

符号付きか符号なしかにかかわらず、結果の計算に必要なビット数が型の範囲を超える場合、オーバーフローと呼ばれます。オーバーフローした上位ビットはサイレントに破棄されます。

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

浮動小数点数

float32float64IEEE 754標準に従ってください。

math.MaxFloat32float32タイプの最大値は次のとおり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语言框架

おすすめ

転載: juejin.im/post/7120939408051994638