Skip to content

基本数据类型

程序实体与关键字

  • 任何Go语言源码文件都由若干个程序实体组成的。在Go语言中,变量、常量、函数、结构体和接口被统称为“程序实体”,而它们的名字被统称为“标识符”。
  • 标识符可以是任何Unicode编码可以表示的字母字符、数字以及下划线“_”。不过,首字母不能是数字或下划线。
  • 注意:在Go语言中,我们对程序实体的访问权限控制只能通过它们的名字来实现。名字首字母为大写的程序实体可以被任何代码包中的代码访问到。而名字首字母为小写的程序实体则只能被同一个代码包中的代码所访问。
  • Go语言还规定了一些特定的字符序列。它们被称为“关键字”。编程人员不能把关键字作为标识符。Go语言的关键字如下表:
用途 关键字
程序声明 import package
程序实体声明和定义 chan const func interface map struct type var
程序流程控制 go select break case continue default derfer else fallthrough for goto if range return switch
package main  // 代码包声明语句。

// 代码包导入语句。
import (
    "fmt"  // 导入代码包fmt。
)

// main函数。
func main() {
    // 打印函数调用语句。用于打印输出信息。
    fmt.Println("Go语言编程实战")
}

变量和常量

我们在这里需要优先说明的是用于声明变量的关键字var,以及用于声明常量的关键字const。要知道,绝大多数的数据类型的值都可以被赋给一个变量,包括函数。而常量则不同,它只能被赋予基本数据类型的值本身。

变量和常量在声明方式方面也有所不同。我们可以在声明一个变量的时候直接为它赋值,也可以只声明不赋值。变量的声明并赋值方式如下:

普通赋值,由关键字var、变量名称、变量类型、特殊标记=,以及相应的值组成。 若只声明不赋值,则去除最后两个组成部分即可。

var num1 int = 1

平行赋值

var num2, num3 int = 2, 3

多行赋值

1
2
3
4
var (
    num4 int = 4
    num5 int = 5
)

上述这三种变量声明的方式,也适用于常量。但是要注意,对于常量不能出现只声明不赋值的情况。

范例:

package main

import (
    "fmt"
)

func main() {
    var (
        num1 int
        num2 int
        num3 int
    )
    num1, num2, num3 = 1, 2, 3
    // 打印函数调用语句。用于打印上述三个变量的值。
    fmt.Println(num1, num2, num3)
}

整数类型的命名和宽度

Go语言的整数类型一共有10个。

其中计算架构相关的整数类型有两个,即:有符号的整数类型 int 和无符号的整数类型 uint

顺便提一下,有符号的整数类型会使用最高位的比特(bit)表示整数的正负。显然,这会对它能表示的整数的范围有一定的损耗(使其缩小)。而无符号的整数类型会使用所有的比特位来表示数值。如此类型的值均为正数。这也是用“无符号的”来形容它们的原因。

言归正传,为什么说这两个整数类型是计算架构相关的呢?这是因为,在不同的计算架构的计算机之上,它们体现的宽度是不同的。宽度即指存储一个某类型的值所需要的空间。空间的单位可以是比特,也可以是字节(byte)。请看下表。

数据类型 计算架构 类型宽度(比特) 类型宽度(字节)
int 32位 32 4
int 64位 64 8
uint 32位 32 4
uint 64位 64 8

以上为对应关系

除了这两个计算架构相关的整数类型之外,还有8个可以显式表达自身宽度的整数类型。如下表所示。

数据类型 有符号 类型宽度(比特)
int8 8
int16 16
int32 32
int64 64
uint8 8
uint16 16
uint32 32
uint64 64

可以看到,这8个整数类型的宽度已经表现在它们的名称中了。

范例:

package main // 代码包声明语句

// 代码包导入语句
import ( 
    "fmt" // 导入代码包fmt。
)

// main函数
func main() { // 代码块由“{”和“}”包裹。

    // 变量声明和赋值语句,由关键字var、变量名num、变量类型uint64、特殊标记=,以及值10组成。
    var num uint64 = 65535 

    // 短变量声明语句,由变量名size、特殊标记:=,以及值组成。
    // 类型uint64的字面上。它的值所需占用的比特数为64,即 8 个字节。
    size := (8)

    // 打印函数调用语句。在这里用于描述一个uint64类型的变量所需占用的比特数。
    // 这里用到了字符串的格式化函数。
    fmt.Printf("类型为 uint64 的整数 %d 需占用的存储空间为 %d 个字节。\n", num, size) 
}

整数类型的表示法

我们已经知道了每个整数类型的宽度,那么它们的宽度意味着什么呢?下面这张表已有所描述。

类型宽度(比特) 数值范围(有符号整数) 数值范围(无符号整数)
8 -128 ~ 127 0 ~ 255
16 -32768 ~ 32767 0 ~ 65535
32 约 -21.47 ~ 21.47亿 约0 ~ 42.94亿
64 约 -922亿亿 ~ 922亿亿 约0 ~ 1844亿亿

如上所示,类型int8的数值表示范围为[-128,127],而类型uint16的数值表示范围为[0,65535],等等。

以我们的常识知道,在数学中整数一般是以10进制来表示的,而在计算机中整数则必是以2进制来表示和存储的。当然,在计算机科学领域,整数的表示形式还包括了8进制和16进制。下面我们就来说说怎样这几种表示法在Go语言中表示整数。

最显而易见的是10进制表示法。如果我们要在声明一个名为num1的int类型变量时给它赋予12这个值,那么这样书写即可:

var num1 int = 12

这是最容易被我们理解的方式了。不过,如果我们要分别以8进制和16进制为变量num1赋值,那么需要:

num1 = 014 // 用“0”作为前缀以表明这是8进制表示法。
或:
num1 = 0xC // 用“0x”作为前缀以表明这是16进制表示法。
📢:(注意,为已声明的变量赋值,无需再在等号左边加入关键字var和变量类型)

范例:

package main

import ( 
    "fmt" 
)

func main() {
    // 声明一个整数类型变量并赋值
    var num1 int = -0x1000 

    // 这里用到了字符串格式化函数。其中,%X用于以16进制显示整数类型值,%d用于以10进制显示整数类型值。
    fmt.Printf("16进制数 %X 表示的是 %d。\n", num1, ( -4096 ))
}

浮点数类型

浮点数类型有两个,即float32float64。你可能已经想到,存储这两个类型的值的空间分别需要4个字节和8个字节。

浮点数类型的值一般由整数部分、小数点“.”和小数部分组成。其中,整数部分和小数部分均由10进制表示法表示。不过还有另一种表示方法。那就是在其中加入指数部分。指数部分由“E”或“e”以及一个带正负号的10进制数组成。比如,3.7E-2表示浮点数0.037。又比如,3.7E+1表示浮点数37

有时候,浮点数类型值的表示也可以被简化。比如,37.0可以被简化为37。又比如,0.037可以被简化为.037

有一点需要注意,在Go语言里,浮点数的相关部分只能由10进制表示法表示,而不能由8进制表示法或16进制表示法表示。比如,03.7表示的一定是浮点数3.7

范例:

package main

import ( 
    "fmt" 
)

func main() {
    // 可以在变量声明并赋值的语句中,省略变量的类型部分。
    // 不过别担心,Go语言可以推导出该变量的类型。
    var num2 = 5.89E-4

    // 这里用到了字符串格式化函数。其中,%E用于以带指数部分的表示法显示浮点数类型值,%f用于以通常的方法显示浮点数类型值。
    fmt.Printf("浮点数 %E 表示的是 %f。\n", num2, ( 0.000589 ))
}

复数类型

复数类型同样有两个,即complex64complex128。存储这两个类型的值的空间分别需要8个字节和16个字节。实际上,complex64类型的值会由两个float32类型的值分别表示复数的实数部分和虚数部分。而complex128类型的值会由两个float64类型的值分别表示复数的实数部分和虚数部分。

复数类型的值一般由浮点数表示的实数部分、加号“+”、浮点数表示的虚数部分,以及小写字母“i”组成。比如,3.7E+1 + 5.98E-2i。正因为复数类型的值由两个浮点数类型值组成,所以其表示法的规则自然需遵从浮点数类型的值表示法的相关规则。

范例:

package main

import ( 
    "fmt" 
)

func main() {
    var num3 = 3.7E+1 + 5.98E-2i

    // 这里用到了字符串格式化函数。其中,%E用于以带指数部分的表示法显示浮点数类型值,%f用于以通常的方法显示浮点数类型值。
    fmt.Printf("浮点数 %E 表示的是 %f。\n", num3, ( 37.000000+0.059800i ))
}

byte 与 rune

byterune类型有一个共性,即:它们都属于别名类型。byteuint8的别名类型,而rune则是int32的别名类型。

byte类型的值需用8个比特位表示,其表示法与uint8类型无异。因此我们就不再这里赘述了。我们下面重点说说rune类型。

一个rune类型的值即可表示一个Unicode字符。Unicode是一个可以表示世界范围内的绝大部分字符的编码规范。关于它的详细信息,大家可以参看其官网(http://unicode.org/)上的文档,或在Google上搜索。用于代表Unicode字符的编码值也被称为Unicode代码点。一个Unicode代码点通常由“U+”和一个以十六进制表示法表示的整数表示。例如,英文字母“A”的Unicode代码点为“U+0041”。

rune类型的值需要由单引号“'”包裹。例如,'A''郝'。这种表示方法一目了然。不过,我们还可以用另外几种形式表示rune类型值。请看下表。

示例 备注 关键字
直接使用Unicode字符 '郝'表示中文字符“郝” 只能表示Unicode支持的字符
“\x”加两个十六进制数 '\x41' 表示英文字母 'A' 只能表示ASCII支持的字符
“\”加三位八进制数 '\101\' 表示英文字母 'A' 只能表示编码值在 [0, 255] 内的字符
“\u”加四位十六进制数 '\u90DD' 表示 “郝” 只能表示编码值在 [0, 65535] 内的字符
“\U”加四位十六进制数 '\U000090DD' 表示 “郝” 可以用于表示任何Unicode字符

大家需要根据实际情况选用上述表示法。在一般情况下,第一种表示法更为通用。因为它是最直观的。不过,在以其他几种方法表示的内容出现在屏幕上的时候,大家也要明白其含义。

另外,在rune类型值的表示中支持几种特殊的字符序列,即:转义符。它们由“\”和一个单个英文字符组成。如下表所示。

转义符 Unicode代码点 说明
\a U+0007 告警铃声或蜂鸣声
\b U+0008 退格符
\f U+000C 换页符
\n U+000A 换行符
\r U+000D 回车符
\t U+0009 水平制表符
\v U+000b 垂直制表符
\ U+005c 反斜杠
\' U+0027 单引号。仅在rune值中有效
'' U+0022 双引号。仅在string值中有效

范例:

package main

import ( 
    "fmt" 
)

func main() {
    // 声明一个rune类型变量并赋值
    var char1 rune = '赞' 

    // 这里用到了字符串格式化函数。其中,%c用于显示rune类型值代表的字符。
    fmt.Printf("字符 '%c' 的Unicode代码点是 %s。\n", char1, ("U+8D5E"))
}

相关Unicode编码表(如:http://unicode-table.com/cn/)。

字符串类型

顾名思义,一个字符串类型的值可以代表一个字符序列。这些字符必须是被Unicode编码规范支持的。虽然从表象上来说是字符序列,但是在底层,一个字符串值却是由若干个字节来表现和存储的。一个字符串(也可以说字符序列)会被Go语言用Unicode编码规范中的UTF-8编码格式编码为字节数组。

注意,我们在一个字符串值或者一个字符串类型的变量之上应用Go语言的内置函数len将会得到代表它的那个字节数组的长度。这可能与我们看到的表象是不同的。

字符串的表示法有两种,即:原生表示法和解释型表示法。若用原生表示法,需用反引号“`”把字符序列包裹起来。若用解释型表示法,则需用双引号“"”包裹字符序列。

二者的区别是,前者表示的值是所见即所得的(除了回车符)。在那对反引号之间的内容就是该字符串值本身。而后者所表示的值中的转义符会起作用并在程序编译期间被转义。所以,如此表示的字符串值的实际值可能会与我们看到的表象不相同。

最后要注意,字符串值是不可变的。也就是说,我们一旦创建了一个此类型的值,就不可能再对它本身做任何修改。

范例:

package main

import ( 
    "fmt" 
)

func main() {
    // 声明一个string类型变量并赋值
    var str1 string = "\\\"" 

    // 这里用到了字符串格式化函数。其中,%q用于显示字符串值的表象值并用双引号包裹。
    fmt.Printf("用解释型字符串表示法表示的 %q 所代表的是 %s。\n", str1, `\"`))
}