Go 语言数据类型

  1. 在 Go 编程语言中,数据类型用于声明函数和变量。
  2. 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
  3. Go 语言按类别有以下几种数据类型:
  •  布尔类型 - 它们是布尔类型,由两个预定义常量组成:(a) true (b) false 
  • 数字类型 - 它们是算术类型,在整个程序中表示:a)整数类型或 b)浮点值
  • 字符串类型 - 字符串类型表示字符串值的集合。它的值是一个字节序列。 字
    符串是不可变的类型,一旦创建后,就不可能改变字符串的内容。预先声明的
    字符串类型是 string 。

派生类型: - 包括(a)指针类型,(b)数组类型,(c)结构类型,(d)联合类型和

  • (e)函数类型(f)切片类型(g)函数类型(h)接口类型(i) 类型

数组类型和结构类型统称为聚合类型。函数的类型指定具有相同参数和结果类型的所有函数的集合。我们将在下一节中看到基本类型,而其他类型将在后续章节中介绍 。

1.数字类型

预定义与体系结构无关的整数类型是:

  • uint8 - 无符号 8 位整数(0 到 255)
  • uint16 - 无符号 16 位整数(0 到 65535)
  • uint32 - 无符号 32 位整数(0 至 4294967295)
  • uint64 - 无符号 64 位整数(0 至 18446744073709551615)
  • int8 - 带符号的 8 位整数(-128 到 127)
  • int16 - 带符号的 16 位整数(-32768 到 32767)
  • int32 - 带符号的 32 位整数(-2147483648 至 2147483647)
  • int64 -  带 符 号 的 64 位 整 数 (-9223372036854775808 至9223372036854775807)

 2.浮点类型

预定义与体系结构无关的整数类型是:

  • float32 - IEEE-754 32 位浮点数
  • float64 - IEEE-754 64 位浮点数
  • complex64 - 复数带有 float32 实部和虚部
  • complex128 - 复数带有 float64 实部和虚部

 n 位整数的值是 n 位,并且使用二进制补码算术运算来表示。

3. 其他数字类型

还有一组具有特定大小的数字类型:

  • byte - 与 uint8 相同
  • rune - 与 int32 相同
  • uint - 32 或 64 位
  • int - 与 uint 大小相同
  • uintptr - 无符号整数,用于存储指针值的未解释位

 

 Go  语言变量

变量只是给程序可以操作的存储区域的名字。Go 中的每个变量都有一个特定的类型,它决定了变量的内存大小和布局; 可以存储在存储器内的值的范围; 以及可以应用于该变量的一组操作。变量的名称可以由字母,数字和下划线字符组成。它必须以字母或下划线开头。大写和小写字母是不同的名称,因为 Go 是区分大小写的。声明变量的一般形式是使用 var 关键字:

var 标识符 变量类型

变量声明

  1. 指定变量类型,声明后若不赋值,使用默认值。
var 变量名 变量类型变量名 = 值
  1. 根据值自行判定变量类型。
var 变量名 = 值
  1. 省略 var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。 这种不带声明格式的只能在函数体中出现
v_name := value

例子:

var a int = 10
var b = 10
c := 10

实例如下:
 

package main

    var a = "wek"
    var b string = "laosiji"
    var c bool

func main(){

    println(a, b, c)

}

结果为:

wek laosiji false

  多变量声明

类型相同多个变量, 非全局变量

var vname1, vname2, vname3 type

vname1, vname2, vname3 = v1, v2, v3

//和 python 很像,不需要显示声明类型,自动推断
var vname1, vname2, vname3 = v1, v2, v3 

//出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误
vname1, vname2, vname3 := v1, v2, v3 

// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)

实例如下:
 

package main
    var x, y int //未赋初值,自动设置为 0
    var ( // 这种因式分解关键字的写法一般用于声明全局变量
    a int
    b bool
    )
    var c, d int = 1, 2
    var e, f = 123, "hello"
    //这种不带声明格式的只能在函数体中出现,
    //g, h := 123, "hello"
func main(){
    g, h := 123, "hello"
    println(x, y, a, b, c, d, e, f, g, h)
}

结果为:

0 0 0 false 1 2 123 hello 123 hello

 值类型和引用类型

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:

go语言判断数据类型 go语言基本数据类型_字符串

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:

go语言判断数据类型 go语言基本数据类型_go语言判断数据类型_02

你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。

go语言判断数据类型 go语言基本数据类型_赋值_03

这个内存地址称之为指针,这个指针实际上也被存在另外的某一个字中。同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。

简短形式,使用 :=  赋值操作符

我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。a 和 b 的类型(int 和 bool)将由编译器自动推断。这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
注意事项

 

  1. 如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,当变量 a在之前已经声明过了以后,又输入了 a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
  2. 如果你在定义变量 a 之前使用 a=20,则会得到编译错误 undefined: a。
  3. 如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:
package main
import "fmt"
func main() {
var a string = "abc"
fmt.Println("hello, world")
}

尝试编译这段代码将得到错误 a declared and not used。

        4.单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用fmt.Println("hello, world", a)会移除错误。

         5.全局变量是允许声明但不使用。 同一类型的多个变量可以声明在同一行,如:var a, b, c int

         6.多变量可以在同一行进行赋值,如:a, b, c = 5, 7, "abc",上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:a, b, c := 5, 7, "abc"。右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。这被称为 并行 或 同时 赋值。

          7.如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a。空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1函数同时得到:val, err = Func1(var1)。

 Go  语言常量

常量是指程序在执行过程中可能不会改变的固定值。 这些固定值也称为文字。常量可以是任何基本数据类型,如整数常量,浮点常量,字符常量或字符串常量。 还有枚举常量。常量一般会被编译器视为常规变量,只是它们的值不能在定义之后被修改。

整型常量
可以是十进制,八进制或十六进制常数。 前缀指定基数:前缀是 0x 或 0X 为十六进制,前缀是 0的为八进制,十进制的前缀则无任何内容。整型常量还可以有一个后缀,它是 U 和 L 的组合,分别用于 unsigned 和 long。后缀可以是大写或小写,可以是任意顺序。这里是一些有效的整型常量的例子:

212          /* 合法 */
215u         /* 合法 */
0xFeeL       /* 合法 */
078          /* 非法: 8 不是 8 进制中的值 */
032UU        /* 非法: 不能添加两个 U */
85           /* 10 进制 */
0213         /* 8 进制 */
0x4b         /* 16 进制 */
30           /* int */
30u          /* unsigned int */
30l          /* long */
30ul         /* unsigned long */

浮点型常量

有整数部分,小数点,小数部分和指数部分。您可以以十进制形式或指数形式来表示浮点文字。在使用十进制形式表示时,必须包括小数点,指数或两者,并且在使用指数形式表示时,必须包括整数部分,小数部分或两者。带符号的指数由 e 或 E 引入。
下面是一些浮点文字的示例:

3.14159      /* 合法 */
314159E-5L   /* 合法 */
510E         /* 非法: 无效的指数 */
210f         /* 非法: 没有小数*/
.e55         /* 非法: 没有整数部分 */

8.3  转义序列
Go 中有一些字符,当它们前面有一个反斜杠,它们将具有特殊的意义,它们用于
表示类似换行符(\n)或制表符(\t)。 这里,有一些这样的转义序列代码的列表:

转义序列       含义
\\            \字符
\'            '字符
\"            "字符
\?            ?字符
\a            警报或响铃
\b            退格
\f            换页
\n            新行
\r            回车
\t            水平制表格
\v            水直制表格
\ooo          八位数字一到三位数
\xhh...       一位或多位的十六进制数

以下是显示几个转义序列字符的示例:

package main

import "fmt"

func main() {
    fmt.Printf("Hello\tWorld!")
}

当上述代码被编译和执行时,它产生以下结果:

 

Hello World!

 字符串常量

字符串文字或常量用双引号""括起来。字符串包含与字符文字类似的字符:纯字符,转义序列和通用字符。可以使用字符串文字将长行拆分为多个行,并使用空格分隔它们。这里是一些字符串文字的例子。下面这三种形式都是相同的字符串。

"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"

常量的定义格式:

const 标识符[类型] = 值

你可以省略类型说明符 [ 类型 ],因为编译器可以根据变量的值来推断其类型。

 

显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"

多个相同类型的声明可以简写为:

 

const c_name1, c_name2 = value1, value2

以下实例演示了常量的应用:

package main

import "fmt"

func main() {
    const LENGTH int = 10
    const WIDTH int = 5
    var area int
    const a, b, c = 1, false, "str" //多重赋值
    area = LENGTH * WIDTH
    fmt.Printf("面积为 : %d", area)
    println()
    println(a, b, c)
}

以上实例运行结果为:

 

面积为 : 50
1 false str

常量还可以用作枚举:

const (
    Unknown = 0
    Female = 1
    Male = 2
)

数字 0、1 和 2 分别代表未知性别、女性和男性。

常量可以用 len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:

package main

import "unsafe"

const (
    a = "abc"
    b = len(a)
    c = unsafe.Sizeof(a)
)
func main(){
    println(a, b, c)
}

以上实例运行结果为:

abc 3 16

 iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。在每一个 const 关键字出现时,被重置为 0,然后再下一个 const 出现之前,每出现一次 iota,其所代表的数字会自动增加 1。iota 可以被用作枚举值:

const (
    a = iota
    b = iota
    c = iota
)

 第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1,c=2 可以简写为如下形式:

const (
    a = iota
    b
    c
)

 iota用法

package main

import "fmt"

func main() {
    const (
        a = iota //0
        b //1
        c //2
        d = "ha" //独立值,iota += 1
        e //"ha" iota += 1
        f = 100 //iota +=1
        g //100 iota +=1
        h = iota //7,恢复计数
        i //8
       )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

以上实例运行结果为:

0 1 2 ha ha 100 100 7 8

 

再看个有趣的的 iota 实例:

package main

import "fmt"

const (
    i=1<<iota
    j=3<<iota
    k
    l
)
func main() {
    fmt.Println("i=",i)
    fmt.Println("j=",j)
    fmt.Println("k=",k)
    fmt.Println("l=",l)
}

 

i= 1
j= 6
k= 12
l= 24

 iota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1(<<表示左移的意思),即:i=1,j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3。

简单表述:

  • i=1:左移 0 位,不变仍为 1;
  • j=3:左移 1 位,变为二进制 110, 即 6;
  • k=3:左移 2 位,变为二进制 1100, 即 12;
  • l=3:左移 2 位,变为二进制 11000,即 24