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。
字面值常量格式化打印
引用類型
引用類型特指 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(表達式)