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(表达式)

猜你喜欢

转载自blog.csdn.net/u011461385/article/details/105860555