这篇文章主要为大家介绍了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
浮点数
float32、float64。遵循IEEE 754标准。
math.MaxFloat32给出了float32类型的最大值:3.4028234663852886e+38。
math.MaxFloat64则是float64类型的最大值:1.7976931348623157e+308。
浮点数可以用来表示小数。
十进制下,float32有效位大约是6位。float64有效位大约是15位。
浮点数的打印可以使用:
%g,保留足够的精度显示
%e,使用指数形式显示
%f,使用无指数形式显示
f := 2.71828 fmt.Printf("%g, %[1]e, %[1]f \n", f) // 2.71828, 2.718280e+00, 2.718280
复数
complex64、complex128 ,二者分别由float32和float64组成。
使用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
发表评论 取消回复