通过示例学习-Go-语言-2023-二-

通过示例学习 Go 语言 2023(二)

Go(Golang)中的所有基本数据类型

来源:golangbyexample.com/all-basic-data-types-golang/

这是 Golang 综合教程系列的第七章。有关该系列其他章节的信息,请参阅此链接 – Golang 综合教程系列

下一教程 – 函数

前一教程 –变量

现在让我们查看当前教程。以下是当前教程的目录。

目录

概述

  • 基本类型

    • 整数

      • 有符号整数

      • 无符号

    • 浮点数

    • 复数

    • 字节

    • 字符

    • 字符串

    • 布尔值

  • 结论

概述

Golang 是一种静态类型的编程语言,意味着每个变量都有一个类型。Go 有几种内置类型。Go 中的数据类型可以分为两种类型。

  1. 基本类型

  2. 复合类型

  • 基本类型

    • 整数

      • 有符号

        • int

        • int8

        • int16

        • int32

        • int64

      • 无符号

        • uint

        • uint8

        • uint16

        • uint32

        • uint64

        • uintptr

    • 浮点数

      • float32

      • float64

    • 复数

      • complex64

      • complex128

    • 字节

    • 字符

    • 字符串

    • 布尔值

  • 复合类型

    • 集合/聚合或非引用类型

      • 数组

      • 结构体

    • 引用类型

      • 切片

      • 映射

      • 通道

      • 指针

      • 函数/方法

    • 接口

      • 空接口的特殊情况

基本类型

在本文中,我们将仅讨论基本类型。

整数

整数可以是有符号或无符号。

有符号整数

有符号整数有 5 种类型,如下所示

类型 大小
int 依赖于平台
int8 8 位/1 字节
int16 16 位/2 字节
int32 32 位/4 字节
int64 64 位/8 字节

int

大小: 依赖于平台。

  • 在 32 位机器上,int 的大小将为 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将为 64 位或 8 字节。

范围:再次取决于平台

  • 在 32 位机器上,int 的大小将为 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将为 64 位或 8 字节。

使用时机:

  • 在使用有符号整数时,建议使用 int,除了下面提到的情况。

    • 当机器是 32 位且所需范围大于-231 到 231-1 时,则使用 int64 而不是 int。请注意,在这种情况下,int64 需要两个 32 位内存地址来共同形成一个 64 位数字。

    • 当范围较小时,使用适当的整数类型。

属性:

  • 声明一个 int
var a int
  • int 是整数的默认类型。当你没有指定类型时,默认类型为 int。
b := 2 //The default is also intfmt.Println(reflect.TypeOf(b)) => int
  • bits包可以帮助了解系统上int的大小
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfIntInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 0r 34
  • unsafe.Sizeof() 函数也可以用于查看 int 的字节大小。

完整工作代码

下面是上述属性的完整工作代码

package main

import (
    "fmt"
    "math/bits"
    "reflect"
    "unsafe"
)

func main() {
    //This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
    sizeOfIntInBits := bits.UintSize
    fmt.Printf("%d bits\n", sizeOfIntInBits)

    var a int
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))

    b := 2
    fmt.Printf("b's typs is %s\n", reflect.TypeOf(b))
}

输出:

64 bits
8 bytes
a's type is int
b's typs is int

int8

大小: 8 位或 1 字节

范围: -2⁷ 到 2⁷ - 1。

何时使用:

  • 当已知整数范围在 -2⁷ 到 2⁷ - 1 之间时,使用 int8。对于临时值,如循环不变量,尽管可能占用更多空间,仍建议使用 int,因为在某些操作或库调用中它可能会被提升为 int。

  • 对于范围在 -27 到 27 - 1 之间的数组值,使用 int8 是一个好的用例。例如,如果你要存储小写字母的 ASCII 索引,则可以使用 int8。

  • 对于数据值,使用 int8 是个好主意。

int16

大小: 16 位或 2 字节

范围: -2¹⁵ 到 2¹⁵ - 1。何时使用:

  • 当已知整数范围在 -2¹⁵ 到 2¹⁵ - 1 之间时,使用 int16。对于临时值,如循环不变量,尽管可能占用更多空间,仍建议使用 int,因为在某些操作或库调用中它可能会被提升为 int。

  • 对于范围在 -215 到 215 - 1 之间的数组值,使用 int8 是一个好的用例。例如,如果你要存储小写字母的 ASCII 索引,则可以使用 int16。

int32

大小: 32 位或 4 字节

范围: -2³¹ 到 2³¹ - 1。

int64

大小: 64 位或 8 字节

范围: -2⁶³ 到 2⁶³ - 1。何时使用:

  • int64 在范围更高时使用。例如,time.Durationint64 类型。

无符号

无符号整数有 5 种类型,如下所示。

类型 大小
uint 平台相关
uint8 8 位/1 字节
uint16 16 位/2 字节
uint32 32 位/4 字节
uint64 64 位/8 字节

uint

大小: 平台相关。

  • 在 32 位机器上,int 的大小将是 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将是 64 位或 8 字节。

范围:再次平台相关。

  • 在 32 位机器上,int 的范围将是 -2³¹ 到 2³¹ - 1。

  • 在 64 位机器上,int 的范围将是 -2⁶³ 到 2⁶³ - 1

何时使用:

  • 每当使用带符号整数时,除了以下提到的情况,使用 uint 是个好主意。

    • 当机器为 32 位且所需范围大于 -2³¹ 到 2³¹ - 1 时,使用 int64 而不是 int。请注意,在这种情况下,int64 由 2 个 32 位内存地址组合而成一个 64 位数字。

    • 当范围较小时,使用适当的 int 类型。

属性:

  • 声明一个 uint
var a uint
  • golang 的 bits 包可以帮助了解系统中 uint 的大小。
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfUintInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 or 64
  • unsafe.Sizeof() 函数也可以用于查看 uint 的字节大小。

完整工作代码

下面是上述属性的完整工作代码

package main

import (
    "fmt"
    "math/bits"
    "reflect"
    "unsafe"
)

func main() {
    //This is computed as const uintSize = 32 << (^uuint(0) >> 32 & 1) // 32 or 64
    sizeOfuintInBits := bits.UintSize
    fmt.Printf("%d bits\n", sizeOfuintInBits)

    var a uint
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

64 bits
8 bytes
a's type is uint

uintptr

这是一种无符号整数类型,足够大以容纳任何指针地址。因此其大小和范围与平台相关。

大小: 平台相关。

  • 在 32 位机器上,int 的大小将是 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将是 64 位或 8 字节。

范围:再次取决于平台

  • 在 32 位机器上,int 的范围将是 -2³¹ 到 2³¹ - 1。

  • 在 64 位机器上,int 的范围将是 -2⁶³ 到 2⁶³ - 1。

属性:

  • uintptr 可以转换为 unsafe.Pointer,反之亦然。

  • 可以对 uintptr 执行算术运算

  • uintptr 尽管它持有指针地址,但仅仅是一个值,并不引用任何对象。因此

    • 如果相应的对象移动,其值不会被更新。例如,当 goroutine 堆栈改变时。

    • 相应的对象可以被垃圾回收。

何时使用:

*** 其目的主要是与 unsafe.Pointer 一起使用,用于不安全的内存访问。

  • 当你想保存指针地址值以进行打印或存储时。由于地址仅被存储并不引用任何对象,相应的对象可以被垃圾回收。

完整工作代码

package main

import (
    "fmt"
    "unsafe"
)

type sample struct {
    a int
    b string
}

func main() {
    s := &sample{a: 1, b: "test"}

   //Getting the address of field b in struct s
    p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))

    //Typecasting it to a string pointer and printing the value of it
    fmt.Println(*(*string)(p))
}

输出

test

uint8

大小: 8 位或 1 字节

范围:0 到 255 或 0 到 2⁸ - 1。何时使用:

  • 当已知 int 的范围将介于 2⁸ - 1 之间时使用 uint8。对于临时值,如循环不变量,尽管可能占用更多空间,仍建议使用 int,因为它在某些操作或库调用中可能会提升为 int。

  • 对于值在 2⁸ - 1 之间的数组,使用 uint8 是一个不错的选择。例如,如果你在数组中存储 ASCII 索引,则可以使用 uint8

uint16

大小: 16 位或 2 字节

范围:0 到 2¹⁶ - 1。何时使用:

  • 当已知 int 的范围将介于 0 到 2¹⁶ - 1 之间时使用 int16。对于临时值,如循环不变量,尽管可能占用更多空间,仍建议使用 int,因为它在某些操作或库调用中可能会提升为 int。

  • 对于值在 -0 到 2¹⁶ - 1 之间的数组,使用 int8 是一个不错的选择。

uint32

大小: 32 位或 4 字节

范围:0 到 2³² - 1

uint64

大小: 64 位或 8 字节

范围:0 到 2⁶⁴ - 1。何时使用:

  • 当范围较高时使用 uint64。

浮点数

浮点数是带有小数的数字。它有两种类型。

类型 大小
float32 32 位或 4 字节
float64 64 位或 8 字节

float64 是默认的浮点类型。当你用小数值初始化一个变量且未指定浮点类型时,推断出的默认类型将是 float64

float32

float32 使用单精度浮点格式来存储值。基本上,它是所有 IEEE-754 32 位浮点数的集合。32 位被分为:1 位符号位、8 位指数位和 23 位尾数。float 32 的大小是 float 64 的一半,并且在某些机器架构上速度相对更快。

大小:32 位或 4 字节

范围:1.2E-38 到 3.4E+38

默认值:0.0

何时使用:

如果在你的系统中内存是瓶颈且范围较小,则可以使用 float32

示例:

以下代码示例说明了以下几点。

  • 声明一个 float32。

  • 打印 float32 的大小(字节)。

代码:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a float32
    var a float32 = 2

    //Size of float32 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

4 bytes
a's type is float32

float64

float64 使用双精度浮点格式来存储值。基本上它是所有 IEEE-754 64 位浮点数的集合。这 64 位分为 1 位符号、11 位指数和 52 位尾数。float64 的大小是 float32 的两倍,但能比 float32 更准确地表示数字。

大小:32 位或 4 字节。

范围:1.2E-38 到 3.4E+38。

默认值:0.0。

何时使用:

当所需精度很高时。

示例:

以下代码示例说明了以下几点。

  • 声明一个 float64。

  • 打印 float64 的大小(字节)。

  • 默认是 float64,当你不指定类型时。

代码:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a float64
    var a float64 = 2

    //Size of float64 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))

    //Default is float64 when you don't specify a type
    b := 2.3
    fmt.Printf("b's type is %s\n", reflect.TypeOf(b))
}

输出:

8 bytes
a's type is float64
b's type is float64

复数

复数有两种类型。

类型 属性
complex64 实部和虚部均为 float32
complex128 实部和虚部均为 float64

默认复数类型是 complex128。

初始化

复数可以通过两种方式初始化。

  • 使用 complex 函数。它具有以下签名。确保 a 和 b 的类型相同,即它们都应为 float32 或都应为 float64。
complext(a, b)
  • 使用简写语法。这在使用直接数字创建复数时使用。如果未指定类型,使用以下方法创建的复数类型将为complex128
a := 5 + 6i

complex64

对于 complex64,实部和虚部均为 float32。

大小:实部和虚部的大小与 float32 相同。它的大小为 32 位或 4 字节。

范围:实部和虚部的范围与 float32 相同,即 1.2E-38 到 3.4E+38。

示例

以下是一个示例代码,显示了。

  • 如何使用上述两种方法创建一个 complex64 数字。

  • 打印一个 complex64 数字的大小。大小将是 8 字节(4 + 4),相当于两个 float32 数字。

  • 打印一个 complex64 数字的类型。

  • 对复数进行+操作。

代码:

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    var a float32 = 3
    var b float32 = 5

    //Initialize-1
    c := complex(a, b)

    //Initialize-2
    var d complex64
    d = 4 + 5i

    //Print Size
    fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
    fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))

    //Print type
    fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
    fmt.Printf("d's type is %s\n", reflect.TypeOf(d))

    //Operations on complex number
    fmt.Println(c+d, c-d, c*d, c/d)
}

输出:

c's size is 8 bytes
d's size is 8 bytes
c's type is complex64
d's type is complex64
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)

complex128

对于 complex128,实部虚部均为float64

大小实部虚部的大小与 float64 相同。它的大小为 64 位或 8 字节。

范围实部虚部的范围与float64相同,即-1.7E+308 到+1.7E+308。

示例

以下是一个示例代码,显示了。

  • 如何使用上述两种方法创建一个 complex128 数字。当未指定类型时,默认类型将是complex128

  • 打印一个 complex128 数字的大小。大小将是 16 字节(8 + 8),相当于两个 float64 数字。

  • 打印一个 complex128 数字的类型。

  • 对复数进行不同操作。

代码:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    var a float64 = 3
    var b float64 = 5

    //Initialize-1
    c := complex(a, b)

    //Initialize-2\. When don't specify a type , the default type will be complex128
    d := 4 + 5i

    //Print Size
    fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
    fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))

    //Print type
    fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
    fmt.Printf("d's type is %s\n", reflect.TypeOf(d))

    //Operations on complex number
    fmt.Println(c+d, c-d, c*d, c/d)
}

输出:

c's size is 16 bytes
d's size is 16 bytes
c's type is complex128
d's type is complex128
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)

字节

Go 中的 byte 是uint8的别名,意味着它是一个整数值。这个整数值为 8 位,表示一个字节(即 0-255 之间的数字)。因此,一个字节可以表示 ASCII 字符。Golang 没有‘char’的数据类型。因此。

  • 字节用于表示 ASCII 字符

  • rune 用于表示所有 UNICODE 字符,包括所有存在的字符。我们将在本教程后面研究 rune。

定义字节

var rbyte byte := 'a'

在声明字节时,我们必须指定类型,就像我们在上面的程序中那样。如果不指定类型,则默认类型为 rune

示例

在下面的代码示例中:

  • 如何定义字节

  • 打印字节类型

  • 打印字节大小

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    var r byte = 'a'

    //Print Size
    fmt.Printf("Size: %d\n", unsafe.Sizeof(r))

    //Print Type
    fmt.Printf("Type: %s\n", reflect.TypeOf(r))

    //Print Character
    fmt.Printf("Character: %c\n", r)
    s := "abc"

    //This will the decimal value of byte
    fmt.Println([]byte(s))
}

输出:

Size: 1
Type: uint8
Character: a
[97 98 99]

Rune

Go 中的 rune 是 int32 的别名,意味着它是一个整数值。这个整数值用于表示一个 Unicode 码点。要理解 rune,你必须知道 Unicode 是什么。下面是简短的描述,但你可以参考著名的博文 – 每个软件开发者绝对、肯定必须了解的关于 Unicode 和字符集的最低限度知识(无借口!)

什么是 Unicode

Unicode 是 ASCII 字符的超集,为每个存在的字符分配一个唯一的数字。这个唯一的数字称为 Unicode 码点。

例如

  • 数字 0 被表示为 Unicode 点 U+0030 (十进制值 – 48)

  • 小写 b 被表示为 Unicode 点 U+0062 (十进制值 – 98)

  • 英镑符号 £ 被表示为 Unicode 点 U+00A3 (十进制值 – 163)

访问 en.wikipedia.org/wiki/List_of_Unicode_characters 以了解其他字符的 Unicode 点。但是 Unicode 不会说明这些码点将如何在内存中保存。这就是 utf-8 进入的地方。

UTF-8

utf-8 使用 1、2、3 或 4 个字节保存每个 Unicode 点。ASCII 点使用 1 个字节存储。这就是为什么 rune 是 int32 的别名,因为在 Go 语言中,Unicode 点最多可以占用 4 个字节,且每个字符串都使用 utf-8 编码。

每个 rune 旨在指代一个 Unicode 点。例如,如果你将一个字符串类型转换为 rune 数组并打印它,那么它将打印出每个字符的 Unicode 点。对于下面的字符串 “0b£”,输出将是 – [U+0030 U+0062 U+00A3]

fmt.Printf("%U\n", []rune("0b£"))

声明 Rune

rune 使用单引号声明,如下所示,声明一个名为 ‘rPound’ 的变量。

rPound := '£'

声明 Rune 后,你还可以执行以下操作:

  • 打印类型 – 输出将是 int32
fmt.Printf("Type: %s\n", reflect.TypeOf(rPound))
  • 打印 Unicode 码点 – 输出将是 U+00A3
fmt.Printf("Unicode CodePoint: %U\n", rPound)
  • 打印字符 – 输出将是 £
fmt.Printf("Character: %c\n", r)

何时使用

当你打算在值中保存 Unicode 码点时,应该使用 rune。当数组中的所有值都表示一个 Unicode 码点时,应该使用 rune 数组。

代码:

以下是说明我们讨论的每个点的代码

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    r := 'a'

    //Print Size
    fmt.Printf("Size: %d\n", unsafe.Sizeof(r))

    //Print Type
    fmt.Printf("Type: %s\n", reflect.TypeOf(r))

    //Print Code Point
    fmt.Printf("Unicode CodePoint: %U\n", r)

    //Print Character
    fmt.Printf("Character: %c\n", r)
    s := "0b£"

    //This will print the Unicode Points
    fmt.Printf("%U\n", []rune(s))

    //This will the decimal value of Unicode Code Point
    fmt.Println([]rune(s))
}

输出:

Size: 4
Type: int32
Unicode CodePoint: U+0061
Character: a
[U+0030 U+0062 U+00A3]
[48 98 163]

字符串

字符串是 golang 中只读的字节切片。字符串可以通过两种方式初始化。

  • 使用双引号 “” 例如 “this”

双引号中的字符串尊重转义序列。例如,如果字符串包含 \n,则打印时会有新行。

  • 使用反引号 eg \this`

反引号中的字符串只是一个原始字符串,它不遵循任何类型的转义序列。

字符串中的每个字符将根据所使用的编码占用一些字节。例如,在 utf-8 编码的字符串中,每个字符将占用 1 到 4 个字节。你可以在这篇必读的著名博客中阅读关于 utf-8 的内容——每个软件开发者绝对、肯定必须了解的 Unicode 和字符集的绝对最少知识(没有借口!)。在 utf-8 中,字符 ab 使用 1 个字节编码,而字符英镑符号 £ 使用 2 个字节编码。因此,当你将字符串“ab£”转换为字节数组并像下面那样打印时,将输出 4 个字节。

s := "ab£"
fmt.Println([]byte(s))

输出

[48 98 194 163]

当你尝试使用 len(“ab£”) 打印上面字符串的长度时,它将输出 4 而不是 3,因为它包含 4 个字节。

还要注意,range 循环遍历每个字符形成的字节序列,因此对于下面的 range 循环

for _, c := range s {
   fmt.Println(string(c))
}

输出将是

a
b
£

对字符串可以执行许多操作。其中一种操作是连接,它将两个字符串结合在一起。连接使用符号‘+’。让我们看看我们讨论过的所有内容的完整工作代码。

代码:

package main
import (
    "fmt"
)
func main() {
    //String in double quotes
    x := "this\nthat"
    fmt.Printf("x is: %s\n", x)

    //String in back quotes
    y := `this\nthat`
    fmt.Printf("y is: %s\n", y)
    s := "ab£"

    //This will print the byte sequence. 
    //Since character a and b occupies 1 byte each and £ character occupies 2 bytes. 
    //The final output will 4 bytes
    fmt.Println([]byte(s))

    //The output will be 4 for same reason as above
    fmt.Println(len(s))

    //range loops over sequences of byte which form each character
    for _, c := range s {
        fmt.Println(string(c))
    }

    //Concatenation
    fmt.Println("c" + "d")
}

输出:

x is: this
that
y is: this\nthat
[97 98 194 163]
4
a
b
£
cd

布尔值

数据类型是 bool,并且有两个可能的值 true 或 false。

默认值:false

操作:

  • 并且 – &&

  • 或者 – ||

  • 取反 – !

示例

以下代码示例展示了

  • 如果未初始化,默认值是 false

  • 上述所有操作在布尔值上

代码

package main

import "fmt"

func main() {
    //Default value will be false it not initialized
    var a bool
    fmt.Printf("a's value is %t\n", a)

    //And operation on one true and other false
    andOperation := 1 < 2 && 1 > 3
    fmt.Printf("Ouput of AND operation on one true and other false %t\n", andOperation)

    //OR operation on one true and other false
    orOperation := 1 < 2 || 1 > 3
    fmt.Printf("Ouput of OR operation on one true and other false: %t\n", orOperation)

    //Negation Operation on a false value
    negationOperation := !(1 > 2)
    fmt.Printf("Ouput of NEGATION operation on false value: %t\n", negationOperation)
}

输出:

a's value is false
Ouput of AND operation on one true and other false false
Ouput of OR operation on one true and other false: true
Ouput of NEGATION operation on false value: true

结论

这就是关于 golang 的基本类型。希望你喜欢这篇文章。请在评论中分享反馈、改进意见或错误。

下一个教程函数

上一个教程变量***

Go 语言中的所有数据结构

来源:golangbyexample.com/all-data-structures-golang/

Go 语言中的数据结构

排序算法

整数

字符串

数组

链表

Golang 中的所有数据类型及示例

来源:golangbyexample.com/all-data-types-in-golang-with-examples/

注意: 如果你对学习 Golang 感兴趣,我们有一个全面的 Golang 教程系列,欢迎查看 – Golang 综合教程系列。现在让我们来看一下当前的教程。以下是目录。

目录

  • 概述

  • 基本类型

    • 整数(有符号和无符号)")

    • 浮点数

    • 复数

    • 字节

    • Rune

    • 字符串

    • 布尔值

  • 复合类型

    • 非引用类型

      • 数组

      • 结构体

    • 引用类型

      • 切片

      • 通道

      • 映射

      • 指针

      • 函数

    • 接口

      • 空接口的特殊情况
  • 结论

概述

Golang 是一种静态类型编程语言,意味着每个变量都有一个类型。Go 有几种内置类型,我们将在本文中探讨。Go 中的数据类型可以分为两种类型。

  1. 基本类型

  2. 复合类型

  • 基本类型

    • 整数

      • 有符号

        • int

        • int8

        • int16

        • int32

        • int64

      • 无符号

        • uint

        • uint8

        • uint16

        • uint32

        • uint64

        • uintptr

    • 浮点数

      • float32

      • float64

    • 复数

      • complex64

      • complex128

    • 字节

    • Rune

    • 字符串

    • 布尔值

  • 复合类型

    • 集合/聚合或非引用类型

      • 数组

      • 结构体

    • 引用类型

      • 切片

      • 映射

      • 通道

      • 指针

      • 函数/方法

    • 接口

      • 空接口的特殊情况

基本类型

让我们先讨论一下 GO 中的基本类型。

整数(有符号和无符号)

整数可以是有符号或无符号。

有符号

有符号整数有如下五种类型:

类型 大小
int 平台依赖
int8 8 位/1 字节
int16 16 位/2 字节
int32 32 位/4 字节
int64 64 位/8 字节

int

大小: 平台依赖。

  • 在 32 位机器上,int 的大小为 32 位或 4 字节。

  • 在 64 位机器上,int 的大小为 64 位或 8 字节。

范围:再次依赖于平台。

  • 在 32 位机器上,int 的范围为-2³¹到 2³¹-1。

  • 在 64 位机器上,int 的范围为-2⁶³到 2⁶³-1。

何时使用:

  • 在使用有符号整数时,建议使用 int,除非在以下提到的情况下。

    • 当机器为 32 位且所需范围大于 -2³¹ 到 2³¹ - 1 时,使用 int64 而不是 int。请注意,在这种情况下,int64 需要 2 个 32 位内存地址来组合成一个 64 位数字。

    • 当范围较小时,使用适当的整数类型。

属性:

  • 声明一个 int
 var a int
  • int 是整数的默认类型。当你不指定类型时,默认类型为 int
b := 2 //The default is also int
fmt.Println(reflect.TypeOf(b)) => int
  • golang 的 bits 包可以帮助了解系统上 int 的大小
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfIntInBits := bits.UintSize
fmt.Println(sizeOfIntInBits) => 32 0r 34
  • unsafe.Sizeof() 函数也可以用来查看 int 的字节大小

完整工作代码

以下是上述属性的完整工作代码

package main

import (
    "fmt"
    "math/bits"
    "reflect"
    "unsafe"
)

func main() {
    //This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
    sizeOfIntInBits := bits.UintSize
    fmt.Printf("%d bits\n", sizeOfIntInBits)

    var a int
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))

    b := 2
    fmt.Printf("b's typs is %s\n", reflect.TypeOf(b))
}

输出:

64 bits
8 bytes
a's type is int
b's typs is int

int8

大小: 8 位或 1 字节

范围:-2⁷ 到 2⁷ - 1。

何时使用:

  • 当已知 int 范围在 -2⁷ 到 2⁷ - 1 之间时使用 int8。对于临时值,如循环不变量,仍然建议使用 int,尽管它可能占用更多空间,因为在某些操作或库调用中,它可能会被提升为 int。

  • 对于值范围在 -27 到 27 - 1 的数组,使用 int8 是一个好用例。例如,如果你存储小写字母的 ASCII 索引,则可以使用 int8。

  • 对于数据值,使用 int8 是一个好主意。

示例:

以下代码示例说明了以下几点

  • 声明一个 int8

  • 打印 int8 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a int 8
    var a int8 = 2

    //Size of int8 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

1 bytes
a's type is int8

int16

大小: 16 位或 2 字节

范围:-2¹⁵ 到 2¹⁵ - 1。

何时使用:

  • 当已知 int 范围在 -2¹⁵ 到 2¹⁵ - 1 之间时使用 int16。对于临时值,如循环不变量,仍然建议使用 int,尽管它可能占用更多空间,因为在某些操作或库调用中,它可能会被提升为 int。

  • 对于值范围在 -215 到 215 - 1 的数组,使用 int8 是一个好用例。例如,如果你存储小写字母的 ASCII 索引,则可以使用 int16。

示例:

以下代码示例说明了以下几点

  • 声明一个 int16

  • 打印 int16 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a int16
    var a int16 = 2

    //Size of int8 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

2 bytes
a's type is int16

int32

大小: 32 位或 4 字节

范围:-2³¹ 到 2³¹ - 1。

示例:

以下代码示例说明了以下几点

  • 声明一个 int32

  • 打印 int8 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a int32
    var a int32 = 2

    //Size of int32 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

4 bytes
a's type is int32

int64

大小: 64 位或 8 字节

范围:-2⁶³ 到 2⁶³ - 1

何时使用:

  • int64 用于范围较高的情况。例如,time.Duration 的类型是 int64

示例:

以下代码示例说明了以下几点

  • 声明一个 int64

  • 打印 int64 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a int64
    var a int64 = 2

    //Size of int64 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

8 bytes
a's type is int64

无符号

无符号整数有 5 种类型,如下所示

类型 大小
uint 平台相关
uint8 8 位/1 字节
uint16 16 位/2 字节
uint32 32 位/4 字节
uint64 64 位/8 字节

uint

大小: 平台相关。

  • 在 32 位机器上,int 的大小将是 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将是 64 位或 8 字节

范围:再次平台相关

  • 在 32 位机器上,int 的范围为 -2³¹ 到 2³¹ - 1。

  • 在 64 位机器上,int 的范围将是 -2⁶³ 到 2⁶³ -1。

何时使用:

  • 在使用其他有符号整数的情况下,建议使用 uint。

    • 当机器为 32 位且所需范围大于 -231 到 231 -1 时,请使用 int64 而不是 int。请注意,在这种情况下,两个 32 位内存地址合成一个 64 位数字。

    • 当范围较小时,请使用适当的 int 类型。

属性:

  • 声明一个 uint。
 var a uint
  • golang 的bits包可以帮助了解系统上uint的大小。
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfUintInBits := bits.UintSize
fmt.Println(sizeOfIntInBits) => 32 or 64
  • unsafe.Sizeof() 函数也可用于查看 uint 的字节大小。

完整工作代码

以下是上述属性的完整工作代码。

package main

import (
    "fmt"
    "math/bits"
    "reflect"
    "unsafe"
)

func main() {
    //This is computed as const uintSize = 32 << (^uuint(0) >> 32 & 1) // 32 or 64
    sizeOfuintInBits := bits.UintSize
    fmt.Printf("%d bits\n", sizeOfuintInBits)

    var a uint
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

64 bits
8 bytes
a's type is uint

uintptr

这是一种无符号整数类型,足够大以容纳任何指针地址。因此,其大小和范围依赖于平台。

大小: 依赖于平台。

  • 在 32 位机器上,int 的大小将为 32 位或 4 字节。

  • 在 64 位机器上,int 的大小将为 64 位或 8 字节。

范围:再次依赖于平台。

  • 在 32 位机器上,int 的范围将是 -2³¹ 到 2³¹ -1。

  • 在 64 位机器上,int 的范围将是 -2⁶³ 到 2⁶³ -1。

属性:

  • uintptr 可以转换为unsafe.Pointer,反之亦然。

  • 可以对 uintptr 执行算术运算。

  • uintptr 虽然存储指针地址,但只是一个值,不引用任何对象。因此。

    • 如果相应对象移动,其值将不会更新。例如,当 goroutine 栈变化时。

    • 相应对象可以被垃圾收集。

何时使用:

***其目的主要是与 unsafe.Pointer 一起使用,以便进行不安全的内存访问。

  • 当你想保存指针地址值以进行打印或存储时。由于地址只是被存储并不引用任何东西,相应的对象可以被垃圾收集。
package main
import (
    "fmt"
    "unsafe"
)
type sample struct {
    a int
    b string
}
func main() {
    s := &sample{a: 1, b: "test"}

   //Getting the address of field b in struct s
    p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))

    //Typecasting it to a string pointer and printing the value of it
    fmt.Println(*(*string)(p))
}

输出:

test

uint8

大小: 8 位或 1 字节。

范围:0 到 255 或 0 到 2⁸ -1。

何时使用:

  • 当已知 int 范围将在 2⁸ -1 之间时,使用 uint8。对于临时值,如循环不变量,尽管可能占用更多空间,但仍建议使用 int,因为在某些操作或库调用中可能会提升为 int。

  • 对于位于 2⁸ -1 之间的数组值,使用 uint8 是一个不错的用例。例如,如果你在数组中存储 ASCII 索引,则可以使用uint8

示例:

以下代码示例说明了以下几点。

  • 声明一个 uint8。

  • 打印 uint8 的字节大小。

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a uint8

    var a uint8 = 2

    //Size of uint8 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

1 bytes
a's type is uint8

uint16

大小: 16 位或 2 字节。

范围:0 到 2¹⁶ -1。

何时使用:

  • 当已知 int 范围将在 0 到 2¹⁶ -1 之间时,使用 int16。对于临时值,如循环不变量,尽管可能占用更多空间,但仍建议使用 int,因为在某些操作或库调用中可能会提升为 int。

  • 对于位于 -0 到 2¹⁶ -1 之间的数组值,使用 int8 是一个不错的用例。

示例:

以下代码示例说明了以下要点

  • 声明一个 uint16

  • 打印 uint16 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    //Declare a uint16
    var a uint16 = 2

    //Size of uint16 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

2 bytes
a's type is uint16

uint32

大小: 32 位或 4 字节

范围:0 到 2³² -1

示例:

以下代码示例说明了以下要点

  • 声明一个 uint32

  • 打印 uint32 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a uint32
    var a uint32 = 2

    //Size of uint32 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

1 bytes
a's type is uint32

uint64

大小: 64 位或 8 字节

范围:0 到 2⁶⁴ -1

使用时机:

  • uint64 在范围更高时使用。

示例:

以下代码示例说明了以下要点

  • 声明一个 uint64

  • 打印 uint64 的字节大小

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a uint64
    var a uint64 = 2

//Size of uint64 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

8 bytes
a's type is uint64

浮点数

浮点数是带小数的数字。它有两种类型

类型 大小
float32 32 位或 4 字节
float64 64 位或 8 字节

float64是默认的浮点类型。当你用小数值初始化变量而不指定浮点类型时,推断的默认类型将是float64

float32

float32使用单精度浮点格式存储值。基本上,它是所有 IEEE-754 32 位浮点数的集合。32 位被划分为– 1 位符号位,8 位指数,和 23 位尾数。float32 占用的空间是 float64 的一半,在某些机器架构上速度相对较快。

大小:32 位或 4 字节

范围:1.2E-38 到 3.4E+38

默认值:0.0

使用时机:

  • 如果系统内存是瓶颈且范围较小,则可以使用float32

示例:

以下代码示例说明了以下要点

  • 声明一个 float32

  • 打印 float32 的字节大小

代码:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a float32
    var a float32 = 2

    //Size of float32 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}

输出:

4 bytes
a's type is float32

float64

float64 使用双精度浮点格式存储值。基本上,它是所有 IEEE-754 64 位浮点数的集合。64 位被划分为– 1 位符号位,11 位指数,52 位尾数。float64 占用的空间是 float32 的两倍,但可以比 float32 更准确地表示数字。

大小:32 位或 4 字节

范围:1.2E-38 到 3.4E+38

默认值:0.0

使用时机:

  • 当需要高精度时

示例:

以下代码示例说明了以下要点

  • 声明一个 float64

  • 打印 float64 的字节大小

  • 当你不指定类型时,默认是 float64

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    //Declare a float64
    var a float64 = 2

    //Size of float64 in bytes
    fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
    fmt.Printf("a's type is %s\n", reflect.TypeOf(a))

    //Default is float64 when you don't specify a type
    b := 2.3
    fmt.Printf("b's type is %s\n", reflect.TypeOf(b))
}

输出:

8 bytes
a's type is float64
b's type is float64

复数

复数有两种类型

类型 属性
complex64 实部和虚部都是 float32
complex128 实部和虚部都是 float64

默认的复数类型是 complex128。

初始化

复数可以通过两种方式初始化

  • 使用复数函数。它具有以下签名。请确保 a 和 b 的类型相同,意味着它们要么都是 float32,要么都是 float64。
complext(a, b)
  • 使用简写语法。这在使用直接数字创建复数时使用。如果未指定类型,则使用下面的方法创建的复数类型将为complex128
a := 5 + 6i

complex64

对于 complex 64,实部和虚部都是 float32

大小:实部和虚部的大小与 float32 相同。大小为 32 位或 4 字节

范围:实部和虚部的范围与 float32 相同,即 1.2E-38 到 3.4E+38

示例

以下是显示的示例代码

  • 如何使用上述两种方法创建 complex64 数字

  • 打印 complex64 数字的大小。大小将为 8 字节(4 + 4),相当于两个 float32 数字

  • 打印 complex64 数字的类型

    • 操作符在复数上

代码:

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    var a float32 = 3
    var b float32 = 5

    //Initialize-1
    c := complex(a, b)

    //Initialize-2
    var d complex64
    d = 4 + 5i

    //Print Size
    fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
    fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))

    //Print type
    fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
    fmt.Printf("d's type is %s\n", reflect.TypeOf(d))

    //Operations on complex number
    fmt.Println(c+d, c-d, c*d, c/d)
}

输出:

c's size is 8 bytes
d's size is 8 bytes
c's type is complex64
d's type is complex64
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)

complex128

对于 complex128,实部和虚部都是 float64

大小:实部和虚部的大小与 float64 相同。大小为 64 位或 8 字节

范围:实部和虚部的范围与 float64 相同,即 -1.7E+308 到 +1.7E+308

示例

以下是显示的示例代码

  • 如何使用上述两种方法创建 complex128 数字。它还显示当类型未指定时,默认类型将为 complex128

  • 打印 complex128 数字的大小。大小将为 16 字节(8 + 8),相当于两个 float64 数字

  • 打印 complex128 数字的类型

  • 对复数进行不同操作

代码:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    var a float64 = 3
    var b float64 = 5

    //Initialize-1
    c := complex(a, b)

    //Initialize-2\. When don't specify a type , the default type will be complex128
    d := 4 + 5i

    //Print Size
    fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
    fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))

    //Print type
    fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
    fmt.Printf("d's type is %s\n", reflect.TypeOf(d))

    //Operations on complex number
    fmt.Println(c+d, c-d, c*d, c/d)
}

输出:

c's size is 16 bytes
d's size is 16 bytes
c's type is complex128
d's type is complex128
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)

字节

Go 中的字节是 uint8 的别名,意味着它是一个整数值。这个整数值为 8 位,表示一个字节,即 0-255 之间的数字。因此,一个字节可以表示 ASCII 字符。Golang 没有 ‘char’ 的数据类型。因此

  • 字节用于表示 ASCII 字符

  • rune 用于表示所有 Unicode 字符,包括所有存在的字符。我们将在本教程后面学习 rune。

定义字节

var rbyte byte := 'a'

在声明字节时,我们必须指定类型,如上面的程序所示。如果不指定类型,则默认类型被视为 rune。

示例

在下面的代码示例中:

  • 如何定义字节

  • 打印字节类型

  • 打印字节大小

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    var r byte = 'a'

    //Print Size
    fmt.Printf("Size: %d\n", unsafe.Sizeof(r))

    //Print Type
    fmt.Printf("Type: %s\n", reflect.TypeOf(r))

    //Print Character
    fmt.Printf("Character: %c\n", r)
    s := "abc"

    //This will the decimal value of byte
    fmt.Println([]byte(s))
}

输出:

Size: 1
Type: uint8
Character: a
[97 98 99]

符文

Go 中的 rune 是 int32 的别名,意味着它是一个整数值。这个整数值用来表示 Unicode 代码点。要理解 rune,您必须了解 Unicode。下面是简短的描述,您可以参考著名的博客文章 – 每个软件开发人员绝对必须了解的 Unicode 和字符集的绝对最小知识(没有借口!)

什么是 Unicode

Unicode 是 ASCII 字符的超集,它为每个存在的字符分配一个唯一的数字。这个唯一的数字称为 Unicode 代码点。

例如

  • 数字 0 表示为 Unicode 点 U+0030 (十进制值 – 48)

  • 小写 b 表示为 Unicode 点 U+0062 (十进制值 – 98)

  • 英镑符号 £ 表示为 Unicode 点 U+00A3 (十进制值 – 163)

访问 en.wikipedia.org/wiki/List_of_Unicode_characters 了解其他字符的 Unicode 点。但是 Unicode 不讨论这些代码点如何保存在内存中。这就是 utf-8 进入的地方。

UTF-8

utf-8 使用 1、2、3 或 4 个字节保存每个 Unicode 点。ASCII 点使用 1 个字节存储。这就是为什么 rune 是 int32 的别名,因为在 Go 中,Unicode 点的最大字节数为 4,而 Go 中的每个字符串都是使用 utf-8 编码的。

每个 rune 旨在指代一个 Unicode 点。例如,如果你在将字符串强制转换为 rune 数组后打印它,则会打印每个字符的 Unicode 点。对于下面的字符串 “0b£”,输出将是 – [U+0030 U+0062 U+00A3]

fmt.Printf("%U\n", []rune("0b£"))

声明 Rune

一个 rune 是通过在单引号中声明的字符,例如声明一个名为 ‘rPound’ 的变量。

rPound := '£'

声明 Rune 后,你还可以执行以下操作。

  • 打印类型 – 输出将是 int32
fmt.Printf("Type: %s\n", reflect.TypeOf(rPound))
  • 打印 Unicode 代码点 – 输出将是 U+00A3
fmt.Printf("Unicode CodePoint: %U\n", rPound)
  • 打印字符 – 输出将是 £
fmt.Printf("Character: %c\n", r)

何时使用

当你打算在值中保存 Unicode 代码点时,应使用一个 rune。当数组中的所有值都旨在表示 Unicode 代码点时,应使用 rune 数组。

代码:

以下是说明我们讨论的每一点的代码。

package main
import (
    "fmt"
    "reflect"
    "unsafe"
)
func main() {
    r := 'a'

    //Print Size
    fmt.Printf("Size: %d\n", unsafe.Sizeof(r))

    //Print Type
    fmt.Printf("Type: %s\n", reflect.TypeOf(r))

    //Print Code Point
    fmt.Printf("Unicode CodePoint: %U\n", r)

    //Print Character
    fmt.Printf("Character: %c\n", r)
    s := "0b£"

    //This will print the Unicode Points
    fmt.Printf("%U\n", []rune(s))

    //This will the decimal value of Unicode Code Point
    fmt.Println([]rune(s))
}

输出:

Size: 4
Type: int32
Unicode CodePoint: U+0061
Character: a
[U+0030 U+0062 U+00A3]
[48 98 163]

字符串

字符串是 golang 中只读的字节切片。字符串可以通过两种方式初始化。

  • 使用双引号 “” 例如 “this”。

双引号中的字符串会遵循转义序列。例如,如果字符串包含 \n,那么在打印时会产生新的一行。

  • 使用反引号 例如 \this`。

反引号中的字符串只是一个原始字符串,不会遵循任何类型的转义序列。

字符串中的每个字符将根据使用的编码占用一些字节。例如,在 utf-8 编码的字符串中,每个字符将占用 1 到 4 个字节。你可以阅读关于 utf-8 的必读博客——每个软件开发者必须了解的 Unicode 和字符集的绝对最小知识(没有借口!)。在 utf-8 中,字符 ab 使用 1 个字节编码,而字符英镑符号 £ 使用 2 个字节编码。因此,字符串 “ab£” 在转换为字节数组并打印时将输出 4 个字节。

s := "ab£"
fmt.Println([]byte(s))

输出

[48 98 194 163]

当你尝试使用 len(“ab£”) 打印上述字符串的长度时,它将输出 4 而不是 3,因为它包含 4 个字节。

还要注意,range 循环遍历每个字符形成的字节序列,因此对于下面的 range 循环。

for _, c := range s {
   fmt.Println(string(c))
}

输出将是

a
b
£

可以对字符串执行许多操作。其中一种操作是连接,它将两个字符串合并。连接使用符号 ‘+’。让我们看看我们讨论的所有内容的完整工作代码。

代码:

package main
import (
    "fmt"
)
func main() {
    //String in double quotes
    x := "this\nthat"
    fmt.Printf("x is: %s\n", x)

    //String in back quotes
    y := `this\nthat`
    fmt.Printf("y is: %s\n", y)
    s := "ab£"

    //This will print the byte sequence. 
    //Since character a and b occupies 1 byte each and £ character occupies 2 bytes. 
    //The final output will 4 bytes
    fmt.Println([]byte(s))

    //The output will be 4 for same reason as above
    fmt.Println(len(s))

    //range loops over sequences of byte which form each character
    for _, c := range s {
        fmt.Println(string(c))
    }

    //Concatenation
    fmt.Println("c" + "d")
}

输出:

x is: this
that
y is: this\nthat
[97 98 194 163]
4
a
b
£
cd

布尔值

数据类型是bool,有两个可能的值:true 或 false。

默认值:false。

操作:

  • 和 – &&

  • 或 – ||

  • 取反 – !

示例

以下代码示例显示了。

  • 如果未初始化,默认值为false

  • 所有上述操作在 bool 上。

代码

package main

import "fmt"

func main() {
    //Default value will be false it not initialized
    var a bool
    fmt.Printf("a's value is %t\n", a)

    //And operation on one true and other false
    andOperation := 1 < 2 && 1 > 3
    fmt.Printf("Ouput of AND operation on one true and other false %t\n", andOperation)

    //OR operation on one true and other false
    orOperation := 1 < 2 || 1 > 3
    fmt.Printf("Ouput of OR operation on one true and other false: %t\n", orOperation)

    //Negation Operation on a false value
    negationOperation := !(1 > 2)
    fmt.Printf("Ouput of NEGATION operation on false value: %t\n", negationOperation)
}

输出:

a's value is false
Ouput of AND operation on one true and other false false
Ouput of OR operation on one true and other false: true
Ouput of NEGATION operation on false value: true

复合类型

非引用类型

数组

Go 中的数组是值。它们是固定长度的同类型序列。由于 Go 中的数组是值,这就是原因。

  • 当你将一个数组赋值给另一个变量时,它会复制整个数组。

  • 当你将数组作为参数传递给一个函数时,它会复制整个数组,而不仅仅是传递地址。

数组的声明如下。假设 N 是数组的大小。

  • 同时指定大小和数值。
newArray := [n]Type{val1, val2, val3}
  • 指定大小 – 无数值。值被设置为该类型的默认零值。
newArray := [len]Type{}

如我们所说,数组的长度是固定的。因此。

  • 我们不能将一个数组赋值给同类型但长度不同的另一个数组。

  • 当你将数组作为函数参数传递时,大小也是其中的一部分。

让我们来看一个数组的例子。以下示例。

  • 演示如何声明一个数组。

  • 将数组作为函数参数传递。

package main

import "fmt"

func main() {
    //Declare a array
    sample := [3]string{"a", "b", "c"}
    print(sample)
}

func print(sample [3]string) {
    fmt.Println(sample)
}

输出:

[a b c]

结构体

在 Go 中,结构体是字段的集合。这些字段可以是不同类型的。结构体作为异构数据类型相关数据的容器。例如,不同的属性用来表示组织中的员工。员工可以有。

  • 字符串类型的名称。

  • 整数类型的年龄。

  • 时间类型的出生日期(DOB)。

..等等。结构体可以用来表示一个员工。

type employee struct {
    name string
    age  int
    dob  time.Time
}

以下程序描述了。

  • 声明一个结构体。

  • 以不同方式初始化结构体。

  • 结构体的大小是其字段大小的总和。

package main
import (
    "fmt"
)
//Declare a struct
type employee struct {
    name   string
    age    int
    salary float64
}
func main() {
    //Initialize a struct without named fields
    employee1 := employee{"John", 21, 1000}
    fmt.Println(employee1)

    //Initialize a struct with named fields
    employee2 := employee{
        name:   "Sam",
        age:    22,
        salary: 1100,
    }
    fmt.Println(employee2)

    //Initializing only some fields. Other values are initialized to default zero value of that type
    employee3 := employee{name: "Tina", age: 24}
    fmt.Println(employee3)
}

输出:

{John 21 1000}
{Sam 22 1100}
{Tina 24 0}

引用类型

切片

切片的大小是动态的,引用数组的元素。如上所述,数组是固定大小的,因此切片为数组提供了更灵活的接口。切片是一种引用类型,因为它内部引用一个数组。它内部由三个字段表示。

  • 指向基础数组的地址。

  • 切片的长度。

  • 切片的容量。

切片的类型由基础数组元素的类型决定,而不是由其长度或容量决定。因此,如果基础数组的类型相同,不论其长度和容量如何,两个切片将具有相同的类型。内置的append函数可以用来向基础数组添加更多值。如果在使用append函数时切片的长度超过当前容量,则分配一个新的切片,容量为当前容量的两倍,当前切片的元素被复制到该新切片中。

内置函数len可以用来获取当前切片的长度,初始化切片。

  • 使用 make – 它帮助你创建一个切片,指定数组的类型、长度和容量。指定长度和容量是可选的。如果指定了长度而未指定容量,则容量将等于长度。
make([]TYPE, length, capacity)
  • 直接初始化。以下示例创建一个字符串的切片。
p := []string{"a", "b", "c"}

切片也可以从数组或其他切片创建。

下面是一个程序,展示了一个切片的示例

  • 使用上述方法声明一个切片

  • 显示追加函数

  • 如何遍历一个切片

package main

import "fmt"

func main() {
    //Declare a slice using make
    s := make([]string, 2, 3)
    fmt.Println(s)

    //Direct intialization
    p := []string{"a", "b", "c"}
    fmt.Println(p)

    //Append function
    p = append(p, "d")
    fmt.Println(p)

    //Iterate over a slcie
    for _, val := range p {
        fmt.Println(val)
    }
}

输出:

[ ]
[a b c]
[a b c d]
a
b
c
d

通道

通道提供了 goroutine 之间的同步和通信。你可以将其视为一个管道,通过这个管道,goroutine 可以发送和接收值。操作符<-用于发送或接收,箭头的方向指定了数据流的方向。

ch <- val    //Sending a value present in var variable to channel
val := <-cha  //Receive a value from  the channel and assign it to val variable

通道有两种类型

  • 无缓冲通道 - 它没有任何容量来存储值,因此

    • 在通道上发送操作会阻塞,除非有另一个 goroutine 来接收。

    • 接收在另一侧有另一个 goroutine 发送之前会阻塞。

  • 缓冲通道 - 你可以在这里指定缓冲区的大小

    • 仅当缓冲区满时,发送到缓冲通道才会阻塞

    • 当通道的缓冲区为空时,接收是唯一会阻塞的操作

一个通道一次只能持有特定类型的数据。在创建通道时,必须在初始化新通道时指定数据类型。在下面的示例中,我们创建了一个持有字符串类型数据的通道。

events := make(chan string)  //Unbuffered channel
events2 := make(chan string, 2)  //Buffered channel of length 2

关闭通道

close() 函数可用于关闭通道。关闭通道意味着不能再向通道发送值

让我们看一个同时使用缓冲和无缓冲通道的工作代码示例

缓冲通道示例:

package main

import "fmt"

func main() {
    //Creating a buffered channel of length 3
    eventsChan := make(chan string, 3)
    eventsChan <- "a"
    eventsChan <- "b"
    eventsChan <- "c"
    //Closing the channel
    close(eventsChan)
    for event := range eventsChan {
        fmt.Println(event)
    }
}

输出:

a
b
c

无缓冲通道示例:

package main

import "fmt"

func main() {
    eventsChan := make(chan string)
    go sendEvents(eventsChan)
    for event := range eventsChan {
        fmt.Println(event)
    }
}

func sendEvents(eventsChan chan<- string) {
    eventsChan <- "a"
    eventsChan <- "b"
    eventsChan <- "c"
    close(eventsChan)
}

输出:

a
b
c

映射

映射是 Go 语言内置的数据类型,类似于哈希,映射键到值。映射是引用数据类型。当你将一个映射赋值给另一个映射时,两个映射都引用同一个底层映射。

零值

映射的零值是 nil

声明

  • 可以使用 var 关键字声明一个映射,并指定其键和值的类型。例如,下面的映射声明了一个名称为
var employeeSalary map[string]int

初始化

  • 使用 make
var employeeSalary make(map[string]int)
  • 使用大括号。你可以在映射中指定映射字面量值,也可以留空的大括号
//Empty braces
employeeSalary := map[string]int{}

//Specify values
employeeSalary := map[string]int{
"John": 1000
"Sam": 2000
} 

操作

  • 添加到一个映射
employeeSalary["John"] = 1000
  • 从映射中获取
salary := employeeSalary["John"]
  • 从映射中删除一个键
delete(employeeSalary, "John")

示例

下面的示例展示了我们讨论过的所有要点

package main

import "fmt"

func main() {
    //Declare
    var employeeSalary map[string]int
    fmt.Println(employeeSalary)

    //Intialize using make
    employeeSalary2 := make(map[string]int)
    fmt.Println(employeeSalary2)

    //Intialize using map lieteral
    employeeSalary3 := map[string]int{
        "John": 1000,
        "Sam":  1200,
    }
    fmt.Println(employeeSalary3)

    //Operations
    //Add
    employeeSalary3["Carl"] = 1500

    //Get
    fmt.Printf("John salary is %d\n", employeeSalary3["John"])

    //Delete
    delete(employeeSalary3, "Carl")

    //Print map
    fmt.Println("\nPrinting employeeSalary3 map")
    fmt.Println(employeeSalary3)
}

输出

map[]
map[]
map[John:1000 Sam:1200]
John salary is 1000

Printing employeeSalary3 map
map[John:1000 Sam:1200]

指针

指针是一个变量,它保存另一个变量的内存地址。指针的零值是 nil。

声明一个指针

在下面的示例中,ex 是 int 指针。

var ex *int

初始化

& 用于获取变量的地址

a := 2
b := &b
  • 运算符可用于解引用指针,这意味着获取存储在指针地址中的值。
fmt.Println(*b) //Print the value stored at address b 

指针也可以使用 new 运算符初始化

a := new(int)
*a = 10
fmt.Println(*a) //Output will be 10

让我们来看一个涵盖上述所有要点的工作代码

package main

import "fmt"

func main() {
    //Declare
    var b *int
    a := 2
    b = &a

    //Will print a address. Output will be different everytime.
    fmt.Println(b)
    fmt.Println(*b)
    b = new(int)
    *b = 10
    fmt.Println(*b) 
}

输出:

0xc000018080
2
10

函数

在 Go 中,函数是值,可以像值一样传递。基本上,函数可以作为一等对象使用并被传递。函数的签名是

func some_func_name(arguments) return_values

函数有一个名称、参数和返回值。此外,请注意 Go 中方法和函数之间的一些重要区别。让我们看看一个方法的签名。

方法:

func (receiver receiver_type) some_func_name(arguments) return_values

从上述签名可以看出,该方法有一个接收者参数。接收者可以是一个结构体或其他任何类型。该方法将访问接收者的属性,并可以调用接收者的其他方法。

下面是一个函数的工作示例。

package main

import "fmt"

func main() {
    add := func(x, y int) int {
        return x + y
    }
    fmt.Println(add(1, 2))
}

func doOperation(fn func(int, int) int, x, y int) int {
    return fn(x, y)
}

输出:

3

接口

接口是 Go 中的一种类型,是方法签名的集合。任何实现了接口所有方法的类型都属于该接口类型。接口的零值是 nil。

接口的签名

type name_of_interface interface{
//Method signature 1
//Method signature 2
}

接口是隐式实现的

类型实现接口没有明确的声明。事实上,在 Go 中并没有类似于 Java 的 “implements” 关键字。如果一个类型实现了接口的所有方法,那么它就实现了该接口。

定义一个接口类型的变量是正确的,如果具体类型实现了接口的所有方法,我们可以将任何具体类型的值分配给该变量。让我们来看一个接口的工作示例。在下面的程序中。

  • 我们声明一个名为 shape 的接口,其中包含一个方法 area

  • square 结构体实现了 area 方法,因此它隐式实现了 shape 接口。

  • 我们声明一个名为 "s" 的 shape 类型变量。

  • s 被分配了具体类型 square 的值。这是可行的,因为 square 结构体实现了 shape 接口的所有方法。

package main

import "fmt"

type shape interface {
    area() int
}

type square struct {
    side int
}

func (s *square) area() int {
    return s.side * s.side
}

func main() {
    var s shape
    s = &square{side: 4}
    fmt.Println(s.area())
}

输出:

16

空接口的特殊情况

一个空接口没有方法,因此默认情况下所有具体类型都实现了空接口。如果您编写一个接受空接口的函数,那么您可以将任何类型传递给该函数。请参见下面的工作代码:

package main

import "fmt"

func main() {
    test("thisisstring")
    test("10")
    test(true)
}

func test(a interface{}) {
    fmt.Printf("(%v, %T)\n", a, a)
}

输出:

(thisisstring, string)
(10, string)
(true, bool)

结论

这就是 Go 中存在的内置数据类型。希望通过阅读本文,您能更好地理解 Go 中的数据类型。

posted @ 2024-10-19 01:44  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报