Go奇技淫巧

判断io读取是否结束,尽量用if n==0这种方式,因为可以判断很多种情况

package main

import (
    "fmt"
    "io"
    "net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello"))
    fmt.Println(r.Header)
}

func main() {
    resp, err := http.Get("http://www.baidu.com")
    if err != nil {
        return
    }
    buf := make([]byte, 4096)
    results := ""
    for {
        n, err := resp.Body.Read(buf)
        if n == 0 { //判断io读取是否结束,尽量用if n==0这种方式,因为可以判断很多种情况
            break
        }
        if err != nil && err != io.EOF {
            fmt.Println(err)
            break
        }
        results += string(buf[:n])
    }
    fmt.Println(results)
}

nil是有类型和值得,var *int = nil,它底层数据结构为(*int,data)

接口为nil的充要条件为接口的运行时类型为nil,并且接口的运行时值为nil。这里将运行时的类型赋值为了File类型的指针

var w io.Writer
var f *os.File
w = f
fmt.Println(w == nil) //false
这里为什么是false呢?原因是当File类型的指针赋值给Writer接口时,接口的类型被赋值为File类型的指针,而类型描述符被赋值为nil,而一个接口为nil的充要条件为接口的运行时类型为nil,并且接口的运行时值为nil。这里将运行时的类型赋值为了File类型的指针,因此是false。这显然不符合我们的预期,换种说法,这显然不是我们想要的结果,那我们如何进行更改呢?只需要将f的编译时类型改为io.Writer即可。因为两种类型是相同的,因此w = f进行赋值的时候,并不会改变运行时类型,又因为两个的值都是nil,所以运行时的值为nil,这样的话w == nil 就显然为true了

当将空指针赋值给接口时,接口实例并不等于nil

当把一个空指针对象赋值给一个interface后,再判断!= nil就不再成立了
代码如下

package main

import "fmt"

type Person interface {
    Name() string
}

type ChenQiongHe struct {
}

func (t *ChenQiongHe) Name() string {
    return "雪山飞猪"
}

func main() {
    var test *ChenQiongHe
    if test == nil {
        fmt.Println("test == nil")
    } else {
        fmt.Println("test != nil")
    }
    //将空指针赋值给接口
    var person Person = test
    if person == nil {
        fmt.Print("person == nil")
    } else {
        fmt.Print("person != nil")
    }
}
运行结果

test == nil
person != nil
test本来是nil,赋值给person后居然不能再用nil判断了

解决方法
使用reflect包的IsNil判断,封装为一个能用方法

func IsNil(i interface{}) bool {
    vi := reflect.ValueOf(i)
    if vi.Kind() == reflect.Ptr {
        return vi.IsNil()
    }
    return false
}

补充知识点:

每当有一个进程启动时,系统会自动打开三个文件:标准输入,标准输出,标准错误--对应三个文件:stdin,stdout,stderr,当运行程序结束,系统自动关闭这三个文件

结构体指针做函数返回值的时候

不能返回局部变量的地址值。局部变量保存在栈帧上,函数调用结束后,栈帧释放,局部变量的地址,不再受系统保护,随时可能分配给其他程序局部变量的值可以返回

内存对齐优化

https://www.cnblogs.com/sunsky303/p/11315429.html

结构体的地址等于首个元素的地址**

package main

import "fmt"

type Person struct {
    name string
    sex  byte
    age  int
}


func main() {
    p := Person{"Jerry", '0', 26}
    fmt.Printf("%p\n", &p)      //0xc000004460
    fmt.Printf("%p\n", &p.name) //0xc000004460
}
用*加类型去调用结构体函数
package main

import (
   "fmt"
)

type N struct {
   int
}

func (n N) test() {
   fmt.Printf("%p\n%d\n", &n, n)
}

func main() {
   var n N = N{2}
   fmt.Printf("%p\n%d\n", &n, n)
   f1 := N.test
   f2 := (*N).test //可用*加类型去调用函数
   f1(n)
   f2(&n)
}
for;true;{} go里面的while true**
var aa bool = true
    c := 0
    for ; aa; {
        fmt.Print(20)
        c++
        if c > 10 {
            aa = false
        }
}
两个函数相同类型相同的条件是拥有相同的形参列表和返回值列表(列表的次序,个数和类型都相同)行参名可以不同,以下两个函数类型是相同的
func add(a,b int) int {
  return a+b
}
func sub(x int,y int) (c int){
  c = x-y;return c
}

可以通过type定义函数类型,函数类型变量可以作为函数的参数和返回值

type Op func(int,int) int
func add(a, b int) int {
  return a+b
}
func do (f Op, a, b int) int{
  return f(a, b)
}

func main(){
  a:=do(add, 1 ,2)
  fmt.print(a)
}
主动调用os.exit(1) defer将不会执行
package main
import "os"
import "fmt"

func main(){
  defer func(){
    fmt.Println("haha")
  }()
  os.Exit(1)
}  //本函数不会打印haha

defer一般放到错误语句检查之后,尽量不要放到循环语句中,相对于普通的函数调用,defer需要间接的数据结构的支持,相对于普通函数有一定的性能损耗





posted @ 2019-12-19 20:15  离地最远的星  阅读(492)  评论(0编辑  收藏  举报