65道Go基础高频题整理(附答案背诵)

说明一下Golang 中 make 和 new 的区别?

好的,关于 makenew 在 Go 语言中的区别,我来解释一下。

  1. new 函数的作用

    • new(T) 函数会为 T 类型的新项分配零值内存,并返回其地址,即一个 *T 类型的值(指针)。这种方法适用于各种类型,包括基本类型如 intfloatbool 等,以及数组、结构体等。
    • 使用 new 分配的内存被初始化为类型的零值,例如 new(int) 返回的指针指向的值会被初始化为 0。
  2. make 函数的作用

    • make 函数仅用于切片(slice)、映射(map)和通道(channel)的内存分配,并返回一个有初始值(非零值)的 T 类型,而不是 *T
    • 对于切片,make 会分配内存并初始化切片的内部结构,如长度和容量等。
    • 对于映射,make 会初始化映射的结构。
    • 对于通道,make 会配置通道的缓冲大小。
  3. 应用场景与例子

    • 使用 new
      • 当你需要一个指向某基本类型零值的指针时,比如在需要在函数间共享或者更改数据时。
      • 例子:num := new(int) 创建了一个指向整数的指针,其值初始化为 0。
    • 使用 make
      • 创建切片、映射或通道时,并且需要它们立即可用,例如要在切片中存储数据,或者在映射中设置键值对。
      • 例子:s := make([]int, 10) 创建了一个长度和容量都是 10 的整数切片。

总结:newmake 都是用于分配内存,但 new 返回的是指针,且适用于所有类型,而 make 只用于切片、映射和通道,并返回一个初始化的(非零值的)类型实例。

说明一下 Golang 数组和切片的区别?

Golang 中数组和切片是两种不同的数据类型,它们有以下几个主要区别:

  1. 长度固定性

    • 数组:数组的长度是固定的,定义时必须指定长度,并且一旦定义,其长度不能更改。
    • 切片:切片是动态的,长度可以改变。切片实际上是对数组的抽象。
  2. 声明方式

    • 数组:声明时需要指定长度,例如 var a [5]int 定义了一个包含 5 个整数的数组。
    • 切片:在声明切片时,不需要指定长度,例如 var s []int 是一个切片,初始时为空。
  3. 内部结构

    • 数组:数组直接存储值,数组的大小和元素类型一起定义了数组的类型。
    • 切片:切片包含三个元素:一个指向底层数组的指针,切片的长度,以及切片的容量。因此,切片比数组更加灵活。
  4. 传递方式

    • 数组:在函数调用时,数组是通过值传递的,这意味着传递的是整个数组的副本。
    • 切片:切片是通过引用传递的,所以函数内部对切片的修改会影响到原切片。
  5. 使用场景

    • 数组:当你需要固定长度的序列时使用数组。例如,你知道你只需要存储 12 个月的名称,可以使用数组。
    • 切片:更加常用,特别是当你需要一个可伸缩的列表时。例如,你在处理一个用户列表,这个列表可能会根据用户注册的增减而变化。
  6. 性能考虑

    • 数组:由于大小固定,内存分配在栈上,因此访问速度快。
    • 切片:更加灵活,但需要小心处理容量和长度的变化,可能会涉及到内存重新分配。

总结:数组是静态的、固定长度的序列,而切片是动态的、可以伸缩的序列。切片在 Go 中更加常用,提供了更高的灵活性和便利性。

使用for range 的时候,它的地址会发生变化吗?

在 Go 语言中,for range 循环的行为取决于你正在遍历的数据类型。对于数组或切片,for range 会提供每个元素的副本,而不是直接引用原始元素。因此,如果你尝试获取这些元素的地址,你会发现地址在每次迭代中都会改变,因为你实际上获取的是副本的地址。

这点可以通过以下的例子来说明:

nums := []int{10, 20, 30}
for i, v := range nums {
    fmt.Printf("Index: %d, Value: %d, Address: %p\n", i, v, &v)
}

在上述代码中,v 是数组元素的副本,而不是直接引用。因此,&v 在每次迭代中都会产生不同的地址,这些地址指向的是副本的位置,而不是原始数据的位置。

但是,如果你直接操作原始数组(或切片)的元素,例如:

nums := []int{10, 20, 30}
for i := range nums {
    fmt.Printf("Index: %d, Value: %d, Address: %p\n", i, nums[i], &nums[i])
}

在这个例子中,&nums[i] 将在每次迭代中提供原始元素的地址,这些地址在每次迭代中都是不变的,因为它们直接引用了原始数据。

总结:在 for range 循环中,如果尝试获取每次迭代中提供的元素的地址,这些地址会在每次迭代中改变,因为这些元素是原始数据的副本。如果你需要操作原始数据的地址,你应该直接引用原始数据。

go defer,多个 defer 的顺序,defer 在什么时机会修改返回值?

defer关键字在Go语言中用于确保函数调用在程序执行完毕后,无论函数是否出现错误,都能正确地被执行。当有多个defer语句在同一函数中,他们会以LIFO(后进先出)的顺序执行。这就意味着在同一个函数内,最后声明的defer语句会被最先执行。

关于修改返回值,defer语句在函数返回之后执行,但是它可以访问并修改返回值。这是因为返回值在函数结束时会被当作defer语句的参数。这意味着如果你在defer函数中修改了返回值,那么实际的返回值会被改变。

以下是一个简单的例子来解释这个概念:

package main

import "fmt"

func main() {
    fmt.Println(deferTest())
}

func deferTest() (result int) {
    defer func() {
        result++
    }()
    return 0
}

在这个例子中,deferTest函数的返回值在没有defer语句的情况下应该是0。但是,因为我们在defer语句中将result增加了1,所以最终返回的值实际上是1。当我们运行main函数,输出结果就是1,而不是0

简要说明一下Golang 单引号,双引号,反引号的区别?

在 Go 语言中,单引号 ('), 双引号 ("), 和反引号 (```) 都被用于表示字符串,但是它们的用途和行为有所不同:

  1. 单引号 (' ') :用于表示单个字符(rune)。它包含的字符必须是 Unicode 编码的字符,例如 'a''中' 或者 Unicode 编码 'u4E2D'。不能用于表示字符串。

    例如:

    var char rune = 'a'
    ```
    
    
  2. 双引号 (" ") :用于表示字符串。字符串是 UTF-8 字符的序列。字符串内的特殊字符可以通过反斜杠 (\) 进行转义。

    例如:

    var str string = "Hello, World!\n"
    ```
    
    
  3. 反引号 (` `) :也用于表示字符串,但是反引号表示的字符串会保留原始内容,包括换行和其他特殊字符,不支持任何转义序列。

    例如:

    var str string = `Hello,
    World!`
    ```
    
    

在这个例子中,反引号字符串会保留原始的换行符。

总结:单引号用于表示字符,双引号和反引号用于表示字符串,但双引号支持转义序列,而反引号保留字符串的原始格式。

Go的函数与方法及方法接受者区别?

在Go语言中,函数和方法都是用来封装代码的,但它们在使用方式上有一些不同。

函数是具有一组输入参数和返回值的独立实体,它不依赖于任何类型。例如:

func Add(a int, b int) int {
    return a + b
}

在这个例子中,Add是一个函数,它接受两个整数作为输入,然后返回两个整数的和。

方法是依赖于特定类型的函数,这个特定类型被称为方法的接收者。例如:

type MyInt int

func (m MyInt) Add(otherInt int) int {
    return int(m) + otherInt
}

在这个例子中,Add是一个方法,它的接收者是MyInt类型。这意味着我们可以在任何MyInt类型的实例上调用Add方法。

方法接收者可以是任何类型的值或者指针,不过不能是一个接口类型或者一个指针类型。接收者的类型在方法名前,以(接收者) 方法名的形式出现。

方法接收者有两种类型:值接收者和指针接收者。值接收者方法在调用时,接收者的值会被复制,而指针接收者在调用方法时,会使用接收者的实际地址,所以可以修改接收者的值。

type MyInt int

func (m MyInt) valueReceiver() {    // 值接收者
    m = 5
}

func (m *MyInt) pointerReceiver() { // 指针接收者
    *m = 5
}

func main() {
    var num MyInt = 2
    num.valueReceiver()
    fmt.Println(num)  // 输出 2
    num.pointerReceiver()
    fmt.Println(num)  // 输出 5
}

在这个例子中,你会看到valueReceiver方法没有改变num的值,而pointerReceiver方法改变了num的值。

能详细介绍一下Golang中的defer底层数据结构和特性吗?

在 Go 语言中,defer 关键字用于推迟一个函数或方法的执行,直到包含该 defer 语句的函数执行完成。这个被延迟的函数被称为 "deferred function"。

defer 的主要特性包括:

  1. 后进先出(LIFO):当在一个函数中存在多个 defer 语句时,它们将会以后进先出的顺序执行。也就是说,最后一个 defer 语句最先被执行,第一个 defer 语句最后被执行。

  2. 参数在 defer 语句中立即求值:在 defer 语句中,函数的参数会立即被计算并保存,而函数本身的执行会被延迟。

  3. 延迟函数的执行时机defer 的函数会在包含 defer 语句的函数返回之前执行,无论这个函数是通过 return 正常结束,还是由于 panic 导致的异常结束。

关于 defer 的底层实现,Go 运行时使用了一个叫做 "deferred function stack" 的结构来管理 defer 调用。这是一个后进先出(LIFO)的栈结构,每当遇到 defer 调用,运行时就会将其添加到当前 goroutine 的 defer 栈中。每个栈帧包含了被推迟函数的指针以及其参数,这些参数在 defer 语句被执行时就已经被求值。

当包含 defer 语句的函数即将返回时,运行时会从 defer 栈中弹出一个栈帧,并执行其中的函数。如果有多个 defer 语句,那么就会按照后进先出的顺序依次执行。

总之,defer 提供了一种强大的机制,可以用来处理资源的清理工作,如关闭文件句柄、解锁互斥锁等,无论函数由于何种原因结束,都能保证资源的正确清理。

能否介绍一下Golang中的slice底层数据结构和特性?

Go语言中的切片(slice)是一种动态数组,它的底层数据结构包括三个关键的属性:指针、长度和容量。

  1. 指针(Pointer):指向数组的第一个元素。
  2. 长度(Length):当前切片的长度,即包含的元素数量。
  3. 容量(Capacity):从切片的开始位置到底层数组的结尾位置的元素数量。

Go语言的切片是引用类型,它们不存储任何数据,只描述底层数组的一段。更改切片的元素会修改其底层数组中的对应元素。

切片的长度和容量可以通过内置的 len()cap() 函数获取。您可以通过 append() 函数向切片添加元素。如果添加的元素数量超过切片的容量,append() 会创建一个新的底层数组,并将原始数据复制到新数组中,然后返回新的切片。

这是一个简单的例子:

package main

import "fmt"

func main() {
    // 创建一个切片
    s := []int{1, 2, 3}
    fmt.Println(len(s), cap(s)) // 输出:3 3

    // 添加一个元素
    s = append(s, 4)
    fmt.Println(len(s), cap(s)) // 输出:4 6
}

在这个例子中,我们首先创建了一个包含三个元素的切片。然后,我们向切片添加了一个元素,因为切片的容量不足以容纳新的元素,所以 append() 创建了一个新的底层数组,并将原始切片的数据复制到新数组中,然后返回了新的切片。新切片的容量是原始切片的容量的两倍。

在Golang中,如何高效地拼接字符串?

在Go语言中,最高效的方式拼接字符串是使用strings.Builderbytes.Buffer。这两者都能够避免在拼接过程中多次创建和复制字符串,因此它们比普通的++=运算符更加高效。

以下是一个使用strings.Builder的例子:

package main

import (
    "strings"
    "fmt"
)

func main() {
    var builder strings.Builder

    for i := 0; i < 10; i++ {
        builder.WriteString("Go ")
    }

    result := builder.String() // Get the concatenated string
    fmt.Println(result)
}

在这个例子中,我们创建了一个strings.Builder对象,并使用WriteString方法向其写入字符串。最后,我们调用String方法获取最终拼接的字符串。

同样,我们可以使用bytes.Buffer达到类似的效果:

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 10; i++ {
        buffer.WriteString("Go ")
    }

    result := buffer.String() // Get the concatenated string
    fmt.Println(result)
}

这两种方式都可以有效地拼接字符串,而不会像++=那样造成大量的内存分配和复制。在处理大量字符串拼接时,这可以带来显著的性能提升。

Golang中2 个 interface 可以比较吗?

在 Go 语言中,两个接口类型的变量可以比较,但有一些规则和限制:

  1. nil 接口比较:两个 nil 接口值是相等的。

  2. 非 nil 接口比较:如果两个接口值的动态类型相同,并且动态值也相等,那么这两个接口值就被认为是相等的。

  3. 限制:如果接口的动态值是不可比较的类型(如切片),那么在尝试比较接口值时将会导致运行时错误。

下面是一些示例:

type Data struct {
    num int
}

var i, j interface{}
fmt.Println(i == j) // 输出:true

i = 10
j = 10
fmt.Println(i == j) // 输出:true

i = Data{num: 10}
j = Data{num: 10}
fmt.Println(i == j) // 输出:true

i = []int{1, 2, 3}
j = []int{1, 2, 3}
fmt.Println(i == j) // 运行时错误:slices can only be compared to nil

总的来说,可以比较两个接口值,但你需要确保接口的动态值是可比较的类型,否则会导致运行时错误。

Golang中init() 函数是什么时候执行的?

在 Go 语言中,init() 函数是一种特殊的函数,它在每个包完成初始化后自动执行。这意味着,你不能在代码中显式地调用 init() 函数,它由 Go 运行时系统自动调用。初始化顺序为:

  1. 如果一个包被导入多次,init() 函数只会被执行一次。

  2. 包的初始化顺序为:首先初始化包级别(Package Level)的变量,然后是 init() 函数。如果一个包导入了其他包,会先初始化被导入的包。

  3. 即使一个包被多个其他包导入,它的 init() 函数也只会被执行一次。

  4. 在同一个包内,多个 init() 函数的执行顺序为它们在 Go 语言中,init() 函数是一种特殊的函数,它在每个包完成初始化后自动执行。这意味着,你不能在代码中显式地调用 init() 函数,它由 Go 运行时系统自动调用。初始化顺序为:

  5. 如果一个包被导入多次,init() 函数只会被执行一次。

  6. 包的初始化顺序为:首先初始化包级别(Package Level)的变量,然后是 init() 函数。如果一个包导入了其他包,会先初始化被导入的包。

  7. 即使一个包被多个其他包导入,它的 init() 函数也只会被执行一次。

  8. 在同一个包内,多个 init() 函数的执行顺序为它们在代码中的出现顺序。

这是一个简单的例子:

package main

import "fmt"

var foo = initFoo()

func initFoo() int {
    fmt.Println("Initializing variable...")
    return 42
}

func init() {
    fmt.Println("Executing init function...")
}

func main() {
    fmt.Println("Executing main function...")
    fmt.Println("Foo:", foo)
}

在这个例子中,程序的输出会是:

Initializing variable...
Executing init function...
Executing main function...
Foo: 42

这显示了 Go 语言初始化的顺序:首先初始化包级别的变量(在这里是 foo),然后执行 init() 函数,最后是 main() 函数。

Golang中如何比较两个 map 是否相等?

Go语言的标准库中没有直接提供比较两个map是否相等的函数,所以我们需要自己编写函数来实现这个功能。在比较两个map是否相等时,我们需要检查两个map的长度是否相等,然后检查每个键在两个map中都存在,并且对应的值也相等。

下面是一个简单的函数,用于比较两个map[string]int是否相等:

package main

import "fmt"

func mapsEqual(a, b map[string]int) bool {
    if len(a) != len(b) {
        return false
    }

    for k, v := range a {
        if vb, ok := b[k]; !ok || vb != v {
            return false
        }
    }

    return true
}

func main() {
    map1 := map[string]int{"one": 1, "two": 2}
    map2 := map[string]int{"one": 1, "two": 2}

    fmt.Println(mapsEqual(map1, map2))  // 输出 true
}

在这个例子中,mapsEqual函数首先检查两个map的长度是否相等。然后,它遍历map a的每个键值对,检查该键在map b中是否存在,以及map b中的值是否与map a中的值相等。如果所有键值对都匹配,那么函数就返回true,表示两个map相等。否则,返回false

是否可以对Golang中的map元素取地址?

在Go语言中,map的元素并不是变量,因此你不能直接获取map元素的地址。尝试这样做会导致编译错误。例如,以下代码将无法编译:

package main

import "fmt"

func main() {
    myMap := map[string]int{
        "foo": 42,
    }

    addr := &myMap["foo"] // 编译错误:cannot take the address of myMap["foo"]
    fmt.Println(addr)
}

这是因为map的元素可能会因为新元素的添加或者map的扩容而被移动,所以直接获取map元素的地址可能会引用到错误的元素。

如果你需要修改map中的元素,你可以使用替换的方式:

package main

import "fmt"

func main() {
    myMap := map[string]int{
        "foo": 42,
    }

    myMap["foo"] = 43
    fmt.Println(myMap) // 输出:map[foo:43]
}

如果你需要一个可以取地址的map元素,你可以将map的值设置为一个包含所需元素的结构体或者指向该元素的指针:

package main

import "fmt"

func main() {
    myMap := map[string]*int{}
    val := 42
    myMap["foo"] = &val

    addr := myMap["foo"]
    fmt.Println(*addr) // 输出:42
}

在这个例子中,我们创建了一个map,它的值是整数指针。然后,我们可以获取和修改这些值的地址。

在Golang中,Map可以边遍历边删除元素吗?

在 Go 语言中,可以在遍历 map 的过程中删除元素。Go 的官方文档明确指出:“在迭代过程中,删除迭代器返回的映射项是安全的。”这是因为,删除操作不会影响迭代器的状态。

以下是一个例子:

m := map[int]string{
    1: "a",
    2: "b",
    3: "c",
    4: "d",
}

for k := range m {
    if k == 1 {
        delete(m, k)
    }
}

在这个例子中,我们在遍历过程中删除了键为 1 的元素,这是完全安全的。

然而,需要注意的是,map 的遍历顺序在 Go 中是不确定的。所以,你不能预测或依赖于遍历的顺序,除非你使用某种方式对键进行排序。

Golang中的float类型可以作为Map的key吗?

在 Go 语言中,浮点类型(float32float64)可以作为 map 的键(key),但这并不是一个好的实践。原因在于浮点数的精度问题和比较的复杂性。

浮点数的比较可能会引入微小的误差,这可能导致意料之外的结果。即使两个浮点数看起来相等,但由于精度问题,它们可能在内存中的表示是不同的。这就意味着,即使你使用看似相同的键来访问 map,你可能得到不同的结果。

这是一个例子:

package main

import "fmt"

func main() {
    m := make(map[float64]string)
    
    m[0.1+0.2] = "value1"
    m[0.3] = "value2"
    
    fmt.Println(m[0.1+0.2]) // 输出:"value1"
    fmt.Println(m[0.3]) // 输出:"value2"
}

尽管 0.1+0.20.3 在数学上是相等的,但在浮点数的世界中,由于精度问题,它们可能不相等。所以,尽管它们看起来应该映射到同一个值,但在这个例子中,它们映射到了两个不同的值。

因此,通常建议使用更稳定、更可预测的数据类型(例如整型或字符串)作为 map 的键。

请问Golang中的map的key为什么是无序的?

Go 语言中的 map 数据结构是基于哈希表实现的。哈希表是一种数据结构,它通过使用哈希函数将键(key)映射到存储位置。这种映射过程使得查找元素的速度非常快,几乎与 map 的大小无关。

然而,这种映射过程并不保证元素的顺序。当你向 map 添加新元素时,如果发生哈希冲突(即两个或更多的 key 被哈希到同一个存储位置),哈希表可能需要重新分配更多的空间,并重新哈希所有的元素以避免冲突。这个过程可能会导致元素的顺序发生变化。

此外,Go 还有意地在每次运行程序时使用不同的哈希种子,以增加 map 的安全性并避免某些类型的攻击。这意味着即使你使用相同的键集合,每次运行程序时 map 的元素顺序也可能会改变。

因此,Go 语言中的 map 数据结构并不保证元素的顺序,遍历 map 的结果是无序的。如果你需要有序的键值对,你可能需要使用其他的数据结构,如排序的切片或者专门的有序 map 库。

能介绍一下Golang中的map的扩容机制吗?

Go语言的map实际上是一个哈希表,它的大小会动态变化。当map的元素数量达到一定阈值时,就会触发扩容操作,即重新分配更大的内存空间,并将所有的元素重新哈希到新的内存空间中。

具体来说,Go语言的map扩容规则如下:

  1. 初始的map大小是0。当第一次添加元素时,map的大小会增加到8。

  2. map的元素数量超过其大小的6.5倍时,或者已经存储的元素数量超过了2^15(32768)时,map的大小会翻倍。

  3. map的最大大小受限于地址空间的大小和指针的大小。在32位系统中,最大大小约为231,而在64位系统中,最大大小约为250。

这种设计使得map在添加新元素时仍能保持较好的性能,但也意味着添加元素可能需要重新哈希所有的元素,这可能会暂时阻塞map的其他操作。

因此,如果你知道最终需要存储的元素数量,那么在创建map时预先设置一个足够大的容量会更加高效,因为这样可以避免多次扩容操作。例如:

myMap := make(map[string]int, 10000)

以上代码创建了一个预期容量为10000的map,如果实际元素数量不超过这个值,那么就不会触发扩容操作。

Golang中Map的数据结构是什么?

Go语言中的 map 是一种哈希表数据结构,也就是键值对的集合。它的底层实现包括数组、哈希函数、链接等关键组成部分。

  1. 数组(Array):数组是 map 的核心组成部分,用于存储键值对(bucket)。每个 bucket 可以存储一到八个键值对。

  2. 哈希函数(Hash Function):哈希函数接收一个键并返回一个整数。这个整数被用来确定键值对应该存储在哪个 bucket 中。

  3. 链接(Linking):如果两个或更多的键哈希到同一个 bucket,那么它们会被链接在一起。这就是所谓的哈希冲突。为了解决这个问题,Go 的 map 使用了链接技术,也就是说,它在同一个 bucket 中存储了一个键值对的链表。

以下是一个简单的例子:

package main

import "fmt"

func main() {
    // 创建一个空的 map
    m := make(map[string]int)

    // 添加键值对
    m["apple"] = 1
    m["banana"] = 2

    // 获取和打印键值对
    fmt.Println(m["apple"])  // 输出:1
    fmt.Println(m["banana"]) // 输出:2
}

在这个例子中,我们创建了一个空的 map,然后添加了两个键值对。当我们从 map 中获取一个值时,Go 使用哈希函数确定键的哈希值,然后在对应的 bucket 中查找键值对。

需要注意的是,Go 的 map 是动态大小的。当向 map 中添加更多的键值对时,如果当前的数组容量不足以容纳更多的数据,Go 就会创建一个新的、更大的数组,并把旧的数据复制到新的数组中。这个过程被称为重新哈希(rehashing)。

在Golang中,任意类型T()都能够调用*T的方法吗?反过来呢?

在 Go 语言中,对于一个给定的类型 T,接收者为 T 的方法可以被 T 类型的值和 *T 类型的指针调用。这是因为 Go 在尝试匹配方法接收者的类型时会自动做取址或解引用的操作。

例如:

type MyType int

func (m MyType) ValueMethod() {
    fmt.Println("ValueMethod called")
}

func (m *MyType) PointerMethod() {
    fmt.Println("PointerMethod called")
}

var v MyType
var p = &v

v.ValueMethod()    // 正常调用
p.ValueMethod()    // 正常调用,Go 自动将 p 解引用

v.PointerMethod()  // 正常调用,Go 自动取 v 的地址
p.PointerMethod()  // 正常调用

然而,如果 T 是一个接口类型,情况就有所不同。接口类型的值不能调用接口类型的指针的方法。

总的来说,对于非接口类型 T*T,它们可以调用彼此的方法。但对于接口类型,值类型接口不能调用指针类型接口的方法。这是因为接口类型的值和指针遵循不同的规则。

由于内容太多,更多内容以链接形势给大家,点击进去就是答案了

20. 请问在Golang中,函数返回局部变量的指针是否安全?

21. 在Golang中,两个nil可能不相等吗?

22. 在Golang中,map赋值的过程是什么样的?

23. Golang如何实现两种 get 操作?

24. Golang的切片作为函数参数是值传递还是引用传递?

25. Golang中哪些不能作为map类型的key?

26. Golang中nil map 和空 map 的区别是什么?

27. 在Golang中,删除一个key后,它的内存会被释放吗?

28. 使用map时需要注意哪些点?是否并发安全?

29. Golang 调用函数传入结构体时,应该传值还是指针?

30. 在Golang中如何解析tag?

31. 简述一下Go的 rune 类型?

32. 能介绍一下sync.Map的用法吗?

33. 在Go语言中,Struct能不能比较 ?

34. 在Go语言中,值接收者和指针接收者的区别是什么?

35. 阐述Go有哪些数据类型?

36. 函数返回局部变量的指针是否安全?

37. 解释array和slice的区别 ?

38. 解释一下,在Go语言中什么是负载因子?

39. Go 语言map和sync.Map谁的性能最好 ?

40. Go 的 chan 底层数据结构和主要使用场景 ?

41. Go 多返回值怎么实现的?

42. Go 中 init 函数的特征?

43. 请说一下Go 中 uintptr 和 unsafe.Pointer 的区别?

44. 简述一下Golang空结构体 struct{} 的使用 ?

45. 简述一下Golang中两个变量值的4种交换方式?

46. 可以修改string类型的值吗?

47. Switch 中如何强制执行下一个 case 代码块 ?

48. 如何关闭 HTTP 的响应体?

49. 当解析 JSON 数据时,默认将数值当做哪种类型?

50. 如何从 panic 中恢复 ?

51. 如何初始化带嵌套结构的结构体 ?

52. 阐述一下Printf()、Sprintf()、Fprintf()函数的区别和用法?

53. 阐述一下Go 如何Array 类型的值作为函数参数 ?

54. 阐述一下Go语言里面的类型断言 ?

55. 在Go语言中,局部变量和全局变量的缺省值是什么?

56. 解释一下Go语言中的静态类型声明 ?

57. 简述一下Golang中的可变参数 ?

58. nil interface 和 nil interface 有什么区别 ?

59. Golang导入包时,为什么可能使用’ _’ /’ .'导入? 举例说明

60. 在Golang中,接口类型是否支持像Java那样的多继承?

61. Golang中的sync包是什么?如何使用?

62. Golang中的sync.WaitGroup是什么?

63. 简述一下Golang的schedule函数 ?

64. 简述一下全局运行队列中获取goroutine ?

65. 简述一下如何从工作线程本地运⾏队列中获取goroutine ?

posted @ 2023-12-18 19:16  帅地  阅读(44)  评论(0编辑  收藏  举报