Skip to content

Instantly share code, notes, and snippets.

@guomaimang
Last active March 29, 2023 14:13
Show Gist options
  • Save guomaimang/5e2dd9b3b202cb43b2de53cb46b859e9 to your computer and use it in GitHub Desktop.
Save guomaimang/5e2dd9b3b202cb43b2de53cb46b859e9 to your computer and use it in GitHub Desktop.
bufio.NewReader(conn) 返回什么

bufio.NewReader(conn) 返回什么

bufio.NewReader(conn) 会返回一个带缓冲的读取器对象,用于从 conn 中读取数据。具体来说,它会返回一个 *bufio.Reader 类型的对象,可以使用该对象的方法从连接中读取数据,例如:

  • Read(p []byte) (n int, err error):从连接中读取数据,并将读取的数据存储到 p 中。该方法会返回读取的字节数 n 和可能发生的错误 err
  • ReadByte() (byte, error):从连接中读取一个字节的数据,并返回读取的字节和可能发生的错误。
  • ReadRune() (rune, int, error):从连接中读取一个 Unicode 字符,并返回该字符的值、字节数和可能发生的错误。
  • ReadString(delim byte) (string, error):从连接中读取数据,直到遇到 delim 字节为止,并返回读取的数据和可能发生的错误。

在读取数据时,bufio.Reader 会使用一个内部缓冲区来减少系统调用的次数,提高读取效率。同时,bufio.Reader 还支持在缓冲区中查找特定的字节序列、按行读取数据等高级操作,使得读取数据更加方便和灵活。

*func auth(reader bufio.Reader, conn net.Conn) (err error) 为什么 reader 使用指针,而conn不使用?

在 Go 中,*bufio.Reader 是一个指针类型,它对应的是一个带缓冲的读取器对象。使用指针类型可以在函数调用时避免对对象进行拷贝,减少内存的开销,提高程序的运行效率。

在函数 auth 中,reader 参数的类型是 *bufio.Reader,也就是一个指向带缓冲的读取器对象的指针。因此,使用指针类型可以避免在函数调用时对读取器对象进行拷贝,提高程序的效率。

conn 参数的类型是 net.Conn,它是一个接口类型,用于表示一个通用的网络连接。在 Go 中,接口类型是一个引用类型,它的值是一个指向实现该接口的对象的指针。因此,在函数 auth 中使用 net.Conn 类型的参数,实际上已经使用了指针,不需要再使用指针类型来传递参数了。

总之,对于带缓冲的读取器对象,使用指针类型可以提高程序的效率;对于接口类型,本身就是引用类型,已经使用了指针,不需要再使用指针类型来传递参数。

bufio.Reader 是什么类型

bufio.Reader 是 Go 标准库 bufio 包中的一个类型,它是一个带缓冲的读取器对象,用于从一个 io.Reader 类型的对象中读取数据。它的完整类型声明为:

type Reader struct {
    // 包含未读取数据的缓冲区
    // 如果缓冲区为空,则填充缓冲区
    // 如果缓冲区已满,则先将缓冲区中的数据写入目标,然后再读取新数据
    buf       []byte

    // 缓冲区中未读取数据的起始位置
    rd        io.Reader // 要从中读取数据的底层 io.Reader 对象
    err       error     // 最近一次读取操作的错误
    lastByte  int       // 最近一次读取操作中读取的最后一个字节
    lastRuneSize int    // 最近一次读取操作中读取的最后一个符文的大小
}

bufio.Reader 可以通过调用其方法来从底层的 io.Reader 中读取数据,并将读取的数据存储在内部缓冲区中。在读取数据时,bufio.Reader 会使用一个内部缓冲区来减少系统调用的次数,提高读取效率。同时,bufio.Reader 还支持在缓冲区中查找特定的字节序列、按行读取数据等高级操作,使得读取数据更加方便和灵活。

bufio.NewReader(conn) 返回什么

bufio.NewReader(conn) 返回的是一个带缓冲的 *bufio.Reader 对象,用于从 conn 中读取数据。具体来说,它会返回一个 *bufio.Reader 类型的指针,可以使用该指针所指向的对象的方法从连接中读取数据,例如:

  • Read(p []byte) (n int, err error):从连接中读取数据,并将读取的数据存储到 p 中。该方法会返回读取的字节数 n 和可能发生的错误 err
  • ReadByte() (byte, error):从连接中读取一个字节的数据,并返回读取的字节和可能发生的错误。
  • ReadRune() (rune, int, error):从连接中读取一个 Unicode 字符,并返回该字符的值、字节数和可能发生的错误。
  • ReadString(delim byte) (string, error):从连接中读取数据,直到遇到 delim 字节为止,并返回读取的数据和可能发生的错误。

在读取数据时,*bufio.Reader 会使用一个内部缓冲区来减少系统调用的次数,提高读取效率。同时,*bufio.Reader 还支持在缓冲区中查找特定的字节序列、按行读取数据等高级操作,使得读取数据更加方便和灵活。

go 是 call by value 还是 call by Reference

在 Go 中,函数调用的参数传递是通过值传递(call by value)的方式实现的。也就是说,当我们调用一个函数时,函数会接收参数的副本而不是参数本身。这意味着,如果在函数内部修改了参数的值,不会影响原始参数的值。如果想要在函数内部修改参数的值,并使得原始参数也发生相应的改变,可以通过传递指针的方式实现。

具体来说,在 Go 中,当我们将一个变量传递给函数时,实际上是将该变量的副本传递给了函数。在函数内部,我们可以对该变量的副本进行修改,但这不会影响到原始变量的值。例如:

func foo(x int) {
    x = x + 1
    fmt.Println("x inside foo:", x)
}

func main() {
    x := 0
    foo(x)
    fmt.Println("x outside foo:", x)
}

在上面的代码中,我们定义了一个 foo 函数,它接收一个整数类型的参数 x,并将 x 的值加 1。在 main 函数中,我们定义了一个整数类型的变量 x,并将其初始化为 0。然后,我们调用 foo 函数,并将变量 x 的值传递给了 foo 函数。在 foo 函数内部,我们修改了变量 x 的值,但这并不会影响到原始变量 x 的值。因此,程序的输出如下:

x inside foo: 1
x outside foo: 0

需要注意的是,在 Go 中,指针也是值类型。如果我们将一个指针传递给函数,实际上是将指针的副本传递给了函数。在函数内部,我们可以通过指针来修改原始变量的值。例如:

func foo(x *int) {
    *x = *x + 1
    fmt.Println("x inside foo:", *x)
}

func main() {
    x := 0
    foo(&x)
    fmt.Println("x outside foo:", x)
}

在上面的代码中,我们定义了一个 foo 函数,它接收一个指向整数类型变量的指针 x,并将 x 的值加 1。在 main 函数中,我们定义了一个整数类型的变量 x,并将其初始化为 0。然后,我们调用 foo 函数,并将变量 x 的地址传递给了 foo 函数。在 foo 函数内部,我们通过指针来修改了变量 x 的值,这也导致原始变量 x 的值发生了相应的改变。因此,程序的输出如下:

x inside foo: 1
x outside foo: 1

总之,在 Go 中,函数调用的参数传递是通过值传递的方式实现的

** 那么,func auth(reader *bufio.Reader, conn net.Conn),对于 conn net.Conn 为何不使用指针? **

在 Go 中,net.Conn 是一个接口类型,用于表示一个通用的网络连接。在函数 auth 中,conn 参数的类型是 net.Conn,这意味着我们可以传递任何实现了 net.Conn 接口的对象作为参数。例如,我们可以传递一个 *net.TCPConn 对象或一个 *net.UDPConn 对象作为参数。

因为 net.Conn 是一个接口类型,它的底层实现可能是一个指针类型,也可能是一个值类型。具体来说,如果我们传递一个值类型的 net.Conn 对象作为参数,函数 auth 会在其内部对该对象进行拷贝,这可能会带来一定的性能开销;如果我们传递一个指针类型的 net.Conn 对象作为参数,函数 auth 则会直接使用该对象的地址,可以避免对对象进行拷贝。

因为 net.Conn 类型的底层实现是不确定的,所以在函数 auth 中,我们不能确定 net.Conn 对象的底层实现是否是一个指针类型。因此,在设计函数 auth 时,我们使用了 net.Conn 类型而不是 *net.Conn 类型,以避免可能出现的类型不匹配的情况。

总之,在 Go 中,对于值类型的对象,我们通常将其传递给函数,而对于指针类型的对象,我们通常将其传递给函数的指针。在函数 auth 中,由于 net.Conn 的底层实现可能是值类型或指针类型,因此我们使用了 net.Conn 类型而不是 *net.Conn 类型。

** func (this *st) Show() 是什么意思 **

在 Go 中,func (this *st) Show() 是一种方法(method)的声明方式,用于定义类型 st 的方法 Show()。具体来说,这个方法的声明包含了以下几个部分:

  • func:关键字,用于声明一个函数或方法。
  • (this *st):方法接收器,用于指定方法作用于哪种类型的对象上。在这里,this 是接收器的名字,*st 是接收器的类型,表示方法作用于一个指向 st 类型的对象的指针上。如果接收器类型是值类型而不是指针类型,则应该写成 (this st)
  • Show():方法名,表示定义的方法的名称。

Show() 方法内部,我们可以通过 this 指针来访问对象的属性和方法,例如:

type st struct {
    name string
    age  int
}

func (this *st) Show() {
    fmt.Printf("name: %s, age: %d\n", this.name, this.age)
}

func main() {
    s := st{name: "Alice", age: 20}
    s.Show()  // 输出:name: Alice, age: 20
}

在上面的代码中,我们定义了一个类型 st,包含两个属性 nameage。然后,我们为类型 st 定义了一个方法 Show(),用于打印对象的属性值。在 main() 函数中,我们创建了一个 st 类型的对象 s,并调用了其方法 Show(),输出对象的属性值。

需要注意的是,在方法接收器中,this 只是一个形参名字,我们可以将其改为其他的名字,例如 selfs 等。但是,通常情况下,我们会使用 thisself 来表示方法接收器,以表明该参数是一个指向对象的指针。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment