Go語言入門-類型

Go語言入門-類型

基本類型

基礎數據類型 類型、長度、默認值、說明介紹

類型 長度 (單位Byte) 默認值 說明
bool 1 false
byte 1 0 同uint8
int 4/8 0 默認的整數類型,具體位數取決於平臺,32bit的OS長度爲4,64位OS長度爲8
uint 4/8 0 無符號整數,具體位數取決於平臺,32bit的OS長度爲4,64位OS長度爲8
int8 1 0 -128~127
uint8 1 0 0~255
int16 2 0 -32768~32767
uint16 2 0 0~65535
int32 4 0 -2147483648~2147483647
uint32 4 0 0~4294967295
int64 8 0 -9223372036854775808~9223372036854775807
uint64 8 0 0~18446744073709551615
float32 4 0.0 1.17549e-038~3.40282e+038
float64 8 0.0 2.22507e-308~1.79769e+308
complex64 8
complex128 16
rune 4 0 int32的別名 存放Unicode編碼
uintptr 4/8 0 存放指針的類型 uint的別名
string “” 字符串
array 數組
struct 結構體
functionnil nil 函數
interface nil 接口
map nil 字典,引用類型
slice nil 切片,引用類型
channel nil 通道,引用類型

字面值常量介紹

整型字面值常量

package main

import "fmt"

func main() {
	//var dec int = 1
	//var oct int = 02
	//var hex int = 0x03
	//var bin int = 0x0101
	var (
		//十進制
		dec int = 127
		//八進制數值需要加前綴0
		oct int = 0177
		//十六進制數值需要加前綴0x
		hex int = 0xDF
		//二進制數值常量需要加前綴0b
		bin int = 0b10000011
	)
	fmt.Printf("dec: [十進制[%d] 八進制[%o] 十六進制[%x] 二進制[%b]\n", dec, dec, dec, dec)
	fmt.Printf("oct: [十進制[%d] 八進制[%o] 十六進制[%x] 二進制[%b]\n", oct, oct, oct, oct)
	fmt.Printf("hex: [十進制[%d] 八進制[%o] 十六進制[%x] 二進制[%b]\n", hex, hex, hex, hex)
	fmt.Printf("bin: [十進制[%d] 八進制[%o] 十六進制[%x] 二進制[%b]\n", bin, bin, bin, bin)
}
/**
output:
dec: [十進制[127] 八進制[177] 十六進制[7f] 二進制[1111111]
oct: [十進制[127] 八進制[177] 十六進制[7f] 二進制[1111111]
hex: [十進制[223] 八進制[337] 十六進制[df] 二進制[11011111]
bin: [十進制[131] 八進制[203] 十六進制[83] 二進制[10000011]
 */

我們可以通過字面值常量來定義數字。例如在剛纔的例子中,可以使用0前綴標識八進制數值如: oct int = 0177、使用0x前綴標識十六進制數值如:hex int = 0xDF, 使用0b前綴標識二進制數值,如bin int = 0b10000011。
同時fmt 包裏的print函數族中Printf支持格式化打印。
其中:
%d 表示十進制佔位符
%o表示八進制佔位符
%x表示十六進制佔位符
%b表示二進制佔位符

浮點型字面值常量


func main() {
	var f32a float32 = 123.334434e-3
	var f32b float32 = -123.3344e-2
	////f32a: [十進制[  0.123334] 十六進制[0x1.f92d88p-04] 二進制[0000000000000000000016553668p-27] 不能直接打印二進制和十六進制
	fmt.Printf("f32a: [十進制[%10f] 十六進制[%08x] 二進制[%032b]\n", f32a, f32a, f32a)
	//f32a: [十進制[  0.123334] 十六進制[3dfc96c4] 二進制[00111101111111001001011011000100]
	fmt.Printf("f32a: [十進制[%10f] 十六進制[%08x] 二進制[%032b]\n", f32a, math.Float32bits(f32a), math.Float32bits(f32a))
	//f32b: [十進制[ -1.233344] 十六進制[bf9dde37] 二進制[10111111100111011101111000110111]
	fmt.Printf("f32b: [十進制[%10f] 十六進制[%08x] 二進制[%032b]\n", f32b, math.Float32bits(f32b), math.Float32bits(f32b))

	var f64c float64 = 123.334434e-3
	fmt.Printf("f64c: [十進制[%10f] 十六進制[%016x] 二進制[%064b]\n", f64c, math.Float64bits(f64c), math.Float64bits(f64c))
}
/**
output:
f32a: [十進制[  0.123334] 十六進制[0x1.f92d88p-04] 二進制[0000000000000000000016553668p-27]
f32a: [十進制[  0.123334] 十六進制[3dfc96c4] 二進制[00111101111111001001011011000100]
f32b: [十進制[ -1.233344] 十六進制[bf9dde37] 二進制[10111111100111011101111000110111]
f64c: [十進制[  0.123334] 十六進制[3fbf92d870802bf1] 二進制[0011111110111111100100101101100001110000100000000010101111110001]
 */

浮點數的字面值常量表示方式有兩種,第一種是普通10進制+小數點標識如:123.01,第二種是科學計數法標識,如1.2301e+3/123010e-3。
同時fmt包中也支持對浮點數的格式打印,其中
使用%f表示浮點數佔用符
如:

	////f32a: [十進制[  0.123334] 十六進制[0x1.f92d88p-04] 二進制[0000000000000000000016553668p-27] 不能直接打印二進制和十六進制
	fmt.Printf("f32a: [十進制[%10f] 十六進制[%08x] 二進制[%032b]\n", f32a, f32a, f32a)

備註:fmt可以使用%032b這種方式進行 數值對齊,%32b標識右對齊長度爲32位,%032b標識右對齊長度爲32位,不足32位用0補全,其他使用請參考官方文檔。

以上代碼中不僅僅打印浮點數值,但是打印的浮點數的十六進制和二進制不是符合預期的,那應該怎麼辦呢?math包中提供一個一組函數:

// Float32bits returns the IEEE 754 binary representation of f,
// with the sign bit of f and the result in the same bit position.
// Float32bits(Float32frombits(x)) == x.
func Float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) }
// Float64bits returns the IEEE 754 binary representation of f,
// with the sign bit of f and the result in the same bit position,
// and Float64bits(Float64frombits(x)) == x.
func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }

該組函數把浮點數轉換爲對應的無符號的整型,並且bit位不發生變化。
上個例子演示一下:

	var f32b float32 = -123.3344e-2
	//f32b: [十進制[ -1.233344] 十六進制[bf9dde37] 二進制[10111111100111011101111000110111]
	fmt.Printf("f32b: [十進制[%10f] 十六進制[%08x] 二進制[%032b]\n", f32b, math.Float32bits(f32b), math.Float32bits(f32b))

	var f64c float64 = 123.334434e-3
	//f64c: [十進制[  0.123334] 十六進制[3fbf92d870802bf1] 二進制[0011111110111111100100101101100001110000100000000010101111110001]
	fmt.Printf("f64c: [十進制[%10f] 十六進制[%016x] 二進制[%064b]\n", f64c, math.Float64bits(f64c), math.Float64bits(f64c))

我們再來分析下Float32bits到底做了什麼。

func Float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) }

通過unsafe包中的Pointer函數獲取入參的指針,然後把該指針轉換爲 *uint32 的指針,然後再獲取該指針的內容(轉換uint32的值)。

布爾值字面值常量

func main() {
	var b bool = true
	var c bool = false
	fmt.Printf("%v\n", b)
	fmt.Printf("%v\n", c)
	//bool值不能轉爲uint值進行後去二進制,本事bool值在棧區是沒有指定的內存空間的。
	fmt.Printf("address:%08x value:%08x\n", (*uint64)(unsafe.Pointer(&b)), *(*uint64)(unsafe.Pointer(&b)))
	fmt.Printf("address:%08x value:%08x\n", (*uint64)(unsafe.Pointer(&c)), *(*uint64)(unsafe.Pointer(&c)))
	fmt.Printf("%t\n", c)
}

/**
true
false
address:c00000a0d8 value:00000001
address:c00000a0d9 value:4300000000000000
false

 */

bool值字面值常量只有true和false,fmt.Printf可以使用%t 或者 %v佔用符進行打印。
注意:

  • 布爾變量的默認值爲false
  • 布爾變量無法進行整型類型轉換,即使通過unsafe包也無法轉換出正確的值。
  • 基於第二點,也能推導出布爾變量無法進行數值計算。
    再看true和false的定義
// true and false are the two untyped boolean values.
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)
	//自定義bool類型
	myTrue := 0 == 0
	//true
	fmt.Println(myTrue)

實際上go語言中bool中 true和false是常量,對應的0 == 0是常量表達式。
我們也可以自己自定義布爾類型。就像上訴例子所表示的。

字符串字面值常量

Go語言中字符串編碼使用UTF-8編碼,字符串的值使用雙引號包裹如:str := “我”。

func main() {
	var str1 string = "hello世界"
	var str2 string = "再見world"
	var str3 string = "hello\t世界"
	var str4 string = `hello\t世界`
	var str5 string = `hello\t世
r
界`
	//hello世界
	fmt.Printf("%s\n", str1)
	//再見world
	fmt.Printf("%s\n", str2)
	//hello	世界
	fmt.Printf("%s\n", str3)
	//hello\t世界
	fmt.Printf("%s\n", str4)
	//hello\t世
	//r
	//界
	fmt.Printf("%s\n", str5)
}
/**
output:
hello世界
再見world
hello	世界
hello\t世界
hello\t世
r
界
 */

以上例子中可以看出定義字符串字面值常量有兩種:

  • 通過雙引號""
    • 雙引號中對於特殊字符需要轉義
    • 雙引號不能支持跨行
  • 通過反引號``
    • 反引號中不會發生轉義
    • 反引號支持跨行

字符字面值常量

字符是組成字符串的單元,通常使用單引號‘’定義字符如:

func main() {
	var a rune = 'w'
	var b byte = 'w'
	var c rune = '我'
	//var d byte = '我'
	fmt.Printf("%c 0x%x\n", a, a)
	fmt.Printf("%c 0x%x\n", b, b)
	fmt.Printf("%c 0x%x\n", c, c)
	fmt.Printf("0x%x\n", '我')
	//constant 25105 overflows byte 如果把非ascill值的字符賦值給 byte則會溢出
	//fmt.Printf("%c\n", d)
}
/**
output:
w 0x77
w 0x77
我 0x6211
0x6211
 */

查看byte和rune的定義

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

byte是uint8的別名
那麼byte的範圍就是 0-255(用於存放ASCILL碼字符)
rune是uint32的別名
那麼uint32的範圍就是0~4294967295(用於存放Unicode字符,兼容ASCILL碼)

- 探索一下rune byte string的關係


func main() {
	str := "hello世界"
	for _, r := range str {
		//通過range遍歷,此時r的類型爲rune
		fmt.Printf("%v(%c)", r, r)
	}
	fmt.Printf("\n")
	for i := 0; i < len(str); i++ {
		//通過下標遍歷,此時str[i]爲byte
		fmt.Printf("%v(%c)", str[i], str[i])
	}
	fmt.Printf("\n")

}
/**
output:
104(h)101(e)108(l)108(l)111(o)19990(世)30028(界)
104(h)101(e)108(l)108(l)111(o)228(ä)184(¸)150(–)231(ç)149(•)140(Œ)
 */

通過以上例子可以看出來,實際字符串的存放是按照字節數組來存放,一個漢字佔用了4個字節。一個普通ascill字符佔用了1個字節。在使用range進行遍歷的時候會把byte轉爲rune,同時3個字節的漢字向上轉換爲rune類型,長度均爲4個byte。

字面值常量格式化打印

參考《golang fmt格式“佔位符”》

引用類型

引用類型特指 slice-切片、map-字典、channel-通道 這三種預定義類型。
同數字、數組等類型相比,引用類型的存儲結構更加複雜。在完成內存分配後還需要進行相關屬性初始化。

引用類型創建

初始化表達式

引用類型聲明,默認值都爲nil

package main

import "fmt"

func main() {
	//slice表達式創建
	var s []int
	fmt.Printf("%#v\n", s)
	//map表達式聲明
	var m map[int]string
	fmt.Printf("%#v\n", m)
	//channel表達式聲明
	var c chan int
	fmt.Printf("%#v\n", c)

}
/**
output:
[]int(nil)
map[int]string(nil)
(chan int)(nil)

*/

其中切片類型、map類型 比較特殊。可以通過初始化表達式進行定義,如:

切片


func main() {
	s := []int {1, 2, 3}
	fmt.Printf("%#v\n", s)
}
/**
output:
[]int{1, 2, 3}
 */

字典


func main() {
	//表達式定義初始化
	m := map[string]int{
		"c" : 13,
		"a" : 10,
		"b" : 11, //最後一個元素需要","逗號結尾
	}
	fmt.Println(m)
}

make函數

引用類型必須使用make函數進行內存分配、初始化一系列屬性。演示:
切片:

func main() {
	//創建一個長度爲3,容量爲10的切片。存放數據的類型爲int,並初始化切片中元素的值爲0
	s := make([]int, 3, 10)
	fmt.Printf("%#v\n", s)
}
/**
output:
[]int{0, 0, 0}
 */

字典:

func main() {
	//創建一個鍵類型爲string, 值類型爲int的字典,該字典的容量
	m := make(map[string]int, 10)
	//創建一個鍵類型爲string,值類型爲int的字典,未指定容量
	m1 := make(map[string]int)
	m["1"] = 1
	m["2"] = 2
	m["3"] = 3

	m1["1"] = 1
	m1["2"] = 2
	m1["3"] = 3
	fmt.Printf("%#v\n", m)
	fmt.Printf("%#v\n", m1)
}
/**
output:
map[string]int{"1":1, "2":2, "3":3}
map[string]int{"1":1, "2":2, "3":3}

 */

通道:

func main() {
	//創建一個無緩存的通道 通道元素類型爲int
 	ch := make(chan int)
 	//穿件一個有緩衝的通道, 通道元素類型爲int
 	ch1 := make(chan int, 10)
 	fmt.Println(ch)
	fmt.Println(ch1)
}
/**
output:
0xc000058060
0xc0000a4000
 */

new函數

函數new按照指定類型長度分配零值內存,返回指針。不關係內構造和初始化方式。
重點
new是用來創建值類型,返回的是一個指針。

func main() {
	//創建一塊內存,用於存放int變量, 並且返回一個int*指針變量,該指針變量指向新分配的內存,並且內存值清爲0
	s := new(int)
	//(*int)(0xc000072090) 0
	fmt.Printf("%#v %#v\n", s, *s)
	//修改s指向內存的值爲10
	*s = 10
	//(*int)(0xc000072090) 10
	fmt.Printf("%#v %#v\n", s, *s)
}
/**
output:
(*int)(0xc000072090) 0
(*int)(0xc000072090) 10
 */

new返回的是指針—只能對值類型有效
new函數操作原始數據

func main() {
	//創建一塊內存,用於存放int數組變量,該數組長度爲10,返回指針,該指針指向新分配的內存,並初始化內存爲0
	arr := new([10]int)
	//&[10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} [10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	fmt.Printf("%#v %#v\n", arr, *arr)
	// 修改下標爲1的數組元素的值爲10
	arr[1] = 10
	//&[10]int{0, 10, 0, 0, 0, 0, 0, 0, 0, 0} [10]int{0, 10, 0, 0, 0, 0, 0, 0, 0, 0}
	fmt.Printf("%#v %#v\n", arr, *arr)
	//通過原始數據arr創建切片s
	s := arr[1:2]
	fmt.Printf("%T %v\n", s, s)
}
/**
&[10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} [10]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
&[10]int{0, 10, 0, 0, 0, 0, 0, 0, 0, 0} [10]int{0, 10, 0, 0, 0, 0, 0, 0, 0, 0}
[]int [10]
 */

通過上面的例子可以看出數組的類型是[N]int N表示數組的長度 切片的類是[]int,沒有長度。一定不要把數組和切片混淆。

未命名類型

數組、切片、字典、通道等類型域具體元素類型或者長度等屬性有關----未命名類型。可以通過type提供具體名稱。可以改變命名類型。
具備相同聲明的未命名類型。
– 待續

類型轉換

除常量、別名類型以及未命名類型外,Go語言必須使用顯示類型轉換。


func main() {
	var b byte = 10
	//顯示類型轉換 小轉大 數據不會溢出
	var i = int(b)
	fmt.Printf("%#v %T\n", b, b)
	fmt.Printf("%#v %T\n", i, i)
}
/**
output:
0xa uint8
10 int
 */

向下轉換,可能會導致數據截斷

func main() {
	var b byte = 10
	//顯示類型轉換 小轉大 數據不會溢出
	var i = int(b)
	fmt.Printf("%#v %T\n", b, b)
	fmt.Printf("%#v %T\n", i, i)
	var tmp = i + 1000
	fmt.Printf("0x%0x %T\n", tmp, tmp)
	//向下轉換高位截斷 0x3f2-->0xf2 顯示轉換數據丟失
	var bb = byte(tmp)
	fmt.Printf("%#v %T\n", bb, bb)
}
/**
0xa uint8
10 int
0x3f2 int
0xf2 uint8
 */

強制轉換的類型從以上例子中可以得出:T(表達式)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章