Go语言基本数据类型

Go语言基本数据类型

1.整型

Go语言同时提供了有符号和无符号类型的整数运算。这里有int8、int16、int32和int64四种截然不同大小的有符号整数类型,分别对应8、16、32、64bit大小的有符号整数,与此对应的是uint8、uint16、uint32和uint64四种无符号整数类型

这里还有两种一般对应特定CPU平台机器字大小的有符号和无符号整数intuint;其中int是应用最广泛的数值类型。这两种类型都有同样的大小,32或64bit,但是我们不能对此做任何的假设;因为不同的编译器即使在相同的硬件平台上可能产生不同的大小

无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合

最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方

算术运算符+、-、*和/可以适用于整数、浮点数和复数,但是取模运算符%仅用于整数间的运算

对于每种类型T,如果转换允许的话,类型转换操作T(x)将x转换为T类型:

go 复制代码
func main() {
	f := 3.141 // a float64
	i := int(f)
	fmt.Println(f, i) // "3.141 3"
	f = 1.99
	fmt.Println(int(f)) // "1"
}

任何大小的整数字面值都可以用以0开始的八进制格式书写,例如0666;或用以0x0X开头的十六进制格式书写,例如0xdeadbeef。十六进制数字可以用大写或小写字母。如今八进制数据通常用于POSIX操作系统上的文件访问权限标志,十六进制数字则更强调数字值的bit位模式

当使用fmt包打印一个数值时,我们可以用%d、%o或%x参数控制输出的进制格式,就像下面的例子:

go 复制代码
func main() {
	o := 0666
	fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
	x := int64(0xdeadbeef)
	fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
	// Output:
	// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
}

请注意fmt的两个使用技巧:

1、通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数

2、第二,%后的#副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀


2.浮点型

Go语言提供了两种精度的浮点数,float32和float64

一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型,因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大

小数点前面或后面的数字都可能被省略(例如.707或1.)。很小或很大的数最好用科学计数法书写,通过e或E来指定指数部分:

go 复制代码
const Avogadro = 6.02214129e23  // 阿伏伽德罗常数
const Planck   = 6.62606957e-34 // 普朗克常数

打印e的幂,打印精度是小数点后三个小数精度和8个字符宽度:

go 复制代码
for x := 0; x < 8; x++ {
    fmt.Printf("x = %d e^x = %8.3f\n", x, math.Exp(float64(x)))
}

3.复数

Go语言提供了两种精度的复数类型:complex64complex128,分别对应float32float64两种浮点数精度。内置的complex函数用于构建复数,内建的realimag函数分别返回复数的实部和虚部:

go 复制代码
func main() {
	var x complex128 = complex(1, 2) // 1+2i
	var y complex128 = complex(3, 4) // 3+4i
	fmt.Println(x * y)               // "(-5+10i)"
	fmt.Println(real(x * y))         // "-5"
	fmt.Println(imag(x * y))         // "10"
}

上面x和y的声明语句还可以简化:

go 复制代码
x := 1 + 2i
y := 3 + 4i

如果一个浮点数面值或一个十进制整数面值后面跟着一个i,例如3.141592i或2i,它将构成一个复数的虚部,复数的实部是0:

go 复制代码
fmt.Println(1i * 1i) // "(-1+0i)"

另外我,复数也可以用==和!=进行相等比较。只有两个复数的实部和虚部都相等的时候它们才是相等的


4.布尔型

一个布尔类型的值只有两种:true和false

布尔值可以和&&(AND)和||(OR)操作符结合,并且有短路行为:如果运算符左边值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值

在Go语言中,布尔值并不会隐式转换为数字值0或1,反之亦然


5.字符串

窥探字符串类型

因为字符串是不可修改的,因此尝试修改字符串内部数据的操作也是被禁止的,例如:

go 复制代码
s[0] = 'L'

不变性意味着如果两个字符串共享相同的底层数据的话也是安全的,这使得复制任何长度的字符串代价是低廉的。同样,一个字符串s和对应的子字符串切片s[7:]的操作也可以安全地共享相同的内存,因此字符串切片操作代价也是低廉的。在这两种情况下都没有必要分配新的内存

在一个双引号包含的字符串面值中,可以用以反斜杠\开头的转义序列插入任意的数据。下面的换行、回车和制表符等是常见的ASCII控制代码的转义方式:

go 复制代码
\a      响铃
\b      退格
\f      换页
\n      换行
\r      回车
\t      制表符
\v      垂直制表符
\'      单引号(只用在 '\'' 形式的rune符号面值中)
\"      双引号(只用在 "..." 形式的字符串面值中)
\\      反斜杠

可以通过十六进制或八进制转义在字符串面值中包含任意的字节。一个十六进制的转义形式是\xhh。一个八进制转义形式是\ooo,包含三个八进制的o数字(0到7)

一个原生的字符串面值形式是这样的,使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行

go 复制代码
func main() {
	const GoUsage = `Go is a tool for managing Go source code.

Usage:
    go command [arguments]
...`
	fmt.Println(GoUsage)
}

字符串内建函数

例如内置的len函数可以返回一个字符串中的字节数目

go 复制代码
func main() {
	s := "hello, world"
	fmt.Println(len(s)) // "12"
	d := "你好,世界"
	fmt.Println(len(d)) // "15"
}

第i个字节并不一定是字符串的第i个字符,因为对于非ASCII字符的UTF8编码会要两个或多个字节

UTF-8编码

UTF8是一个将Unicode码点编码为字节序列的变长编码。如果第一个字节的高端bit为0,则表示对应7bit的ASCII字符,ASCII字符每个字符依然是一个字节,和传统的ASCII编码兼容。如果第一个字节的高端bit是110,则说明需要2个字节;后续的每个高端bit都以10开头。更大的Unicode码点也是采用类似的策略处理

变长的编码无法直接通过索引来访问第n个字符,但是UTF8编码获得了很多额外的优点。首先UTF8编码比较紧凑,完全兼容ASCII码,并且可以自动同步:它可以通过向前回朔最多3个字节就能确定当前字符编码的开始字节的位置

如果我们真的关心每个Unicode字符,我们可以使用其它处理方式。考虑下面的这个例子中的字符串,它混合了中西两种字符,为了处理这些真实的字符,我们需要一个UTF8解码器。unicode/utf8包提供了该功能,我们可以这样使用:

go 复制代码
func main() {
	s := "Hello, 世界"
	for i := 0; i < len(s); {
		r, size := utf8.DecodeRuneInString(s[i:])
		fmt.Printf("%d\t%c\n", i, r)
		i += size
	}
}

字符串处理相关的四个包

标准库中有四个包对字符串处理尤为重要:bytes、strings、strconv和unicode包

1、strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能

2、bytes包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的[]byte类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用bytes.Buffer类型将会更有效

3、strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换

4、unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,它们用于给字符分类

字符串和数字的转换

将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa("整数到ASCII")

go 复制代码
func main() {
	x := 123
	y := fmt.Sprintf("%d", x)
	fmt.Println(y, strconv.Itoa(x)) // "123 123"
}

如果要将一个字符串解析为整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数:

go 复制代码
func main() {
	x, _ := strconv.Atoi("123")
	y, _ := strconv.ParseInt("123", 10, 64)
	fmt.Println(x)
	fmt.Println(y)
}

ParseInt函数的第三个参数是用于指定整型数的大小;例如16表示int16,0则表示int。在任何情况下,返回的结果y总是int64类型,你可以通过强制类型转换将它转为更小的整数类型


6.常量

一个常量的声明语句定义了常量的名字,和变量的声明语法类似,常量的值不可修改,这样可以防止在运行期被意外或恶意的修改

所有常量的运算都可以在编译期完成,这样可以减少运行时的工作,也方便其他编译优化。当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除零、字符串索引越界、任何导致无效浮点数的操作等

常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof

iota 常量生成器

常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一,例如:

go 复制代码
type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

周日将对应0,周一为1,如此等等

我们也可以在复杂的常量表达式中使用iota,下面是一个更复杂的例子,每个常量都是1024的幂:

go 复制代码
const (
    _ = 1 << (10 * iota)
    KiB // 1024
    MiB // 1048576
    GiB // 1073741824
    TiB // 1099511627776             (exceeds 1 << 32)
    PiB // 1125899906842624
    EiB // 1152921504606846976
    ZiB // 1180591620717411303424    (exceeds 1 << 64)
    YiB // 1208925819614629174706176
)
相关推荐
blammmp26 分钟前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧39 分钟前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
暗黑起源喵1 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong1 小时前
Java反射
java·开发语言·反射
Troc_wangpeng1 小时前
R language 关于二维平面直角坐标系的制作
开发语言·机器学习
努力的家伙是不讨厌的1 小时前
解析json导出csv或者直接入库
开发语言·python·json
Envyᥫᩣ1 小时前
C#语言:从入门到精通
开发语言·c#
童先生1 小时前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
lulu_gh_yu1 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚2 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea