golang语法要点笔记

golang学习笔记

读《go学习笔记第四版》 《学习go语言》 《gopl-zh》《Go语言实战》记录

 

多变量赋值时,先计算所有相关值,然后再从左到右依次赋值。

data, i := [3]int{1, 2, 3}, 0
i, data[i] = 2, 6
fmt.Println(i)  //2
fmt.Println(data)  //[6 2 3]

 

用{}区分代码块

 

常量值必须是编译期可确定的数字、字符串、布尔值。 未使用局部常量不会引发编译错误。 

const (    
    _ = iota                  // iota = 0
    KB int64 = 1 << (10 * iota)      // iota = 1   
    MB                          // 与 KB 表达式相同,但 iota = 2
    GB
    TB
 )

 

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

可以简写

const (
    a = iota
    b
    c
)
 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
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)
}
//output:
i= 1
j= 6
k= 12
l= 24

 

var s []int    // len(s) == 0, s == nil
s = nil        // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{}    // len(s) == 0, s != nil

  

map中的元素并不是一个变量,不能对map的元素进行取址操作

 

 

 range 会复制对象

a := [3]int{0, 1, 2}
for i, v := range a {    // index、value 都是从复制品中取出。
    if i == 0 {    // 在修改前,我们先修改原数组。
    a[1], a[2] = 999, 999
     fmt.Println(a)    // 确认修改有效,输出 [0, 999, 999]。
    }
    a[i] = v + 100    // 使⽤用复制品中取出的 value 修改原数组。 
}
fmt.Println(a)                    // 输出 [100, 101, 102]。
s := []int{1, 2, 3, 4, 5}
for i := range s {       // 复制 struct slice { pointer, len, cap }。
    if i == 0 {
     s = s[:3]           // 对 slice 的修改,不会影响 range。 
    s[2] = 100          // 对底层数据的修改。    
    }
}
fmt.Println(s)  //[1 2 100 4 5]
//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
for i, c := range "go" {
    fmt.Println(i, c)
}

 

var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"

相当于

(*employeeOfTheMonth).Position += " (proactive team player)"

 



var c = [...]int {1, 2, 3, 4, 5} //由初始化列表决定数组长度,不可省去标识符 "...",否则将变成切片Slice


    x := []int{1, 2, 3}
    i := 2
    switch i {
    case x[1]:
        println("a")
    case 1, 3:    //同时匹配1,3
        println("b")
    default:
        println("c")
    }
switch i := x[2]; {           // 带初始化语句
    case i > 0:
        println("a")
    case i < 0:
        println("b")
    default:
        println("c")
}

MAP

  /* map插入key - value对,各个国家对应的首都 */
    countryCapitalMap [ "France" ] = "Paris"
    countryCapitalMap [ "Italy" ] = "罗马"
    countryCapitalMap [ "Japan" ] = "东京"
    countryCapitalMap [ "India " ] = "新德里"

    /*使用键输出地图值 */ for country := range countryCapitalMap {
        fmt.Println(country, "首都是", countryCapitalMap [country])
    }

    /*查看元素在集合中是否存在 */
    captial, ok := countryCapitalMap [ "美国" ] /*如果确定是真实的,则存在,否则不存在 */
    /*fmt.Println(captial) */
    /*fmt.Println(ok) */
    if (ok) {
        fmt.Println("美国的首都是", captial)
    } else {
        fmt.Println("美国的首都不存在")
    }

 

break 可用于 for、switch、select,而 continue 仅能用于 for 循环。

type User struct {
    id   int
    name string
}

func (self *User) Test() {
    fmt.Printf("%p, %v\n", self, self)
}
func main() {
    u := User{1, "Tom"}
    u.Test()
    mValue := u.Test
    mValue() // 隐式传递 receiver
    mExpression := (*User).Test
    mExpression(&u) // 显式传递 receiver
}
/*
0x210230000, &{1 Tom}
0x210230000, &{1 Tom}
0x210230000, &{1 Tom}
*/
type User struct {
    id   int
    name string
}

func (self User) Test() {
    fmt.Println(self)
}
func main() {
    u := User{1, "Tom"}
    mValue := u.Test // ⽴立即复制 receiver,因为不是指针类型,不受后续修改影响。
    u.id, u.name = 2, "Jack"
    u.Test()
    mValue()
}
/*
{2 Jack} 
{1 Tom}
*/
func (self *User) TestPointer() {
    fmt.Printf("TestPointer: %p, %v\n", self, self)
}
func (self User) TestValue() {
    fmt.Printf("TestValue: %p, %v\n", &self, self)
}
func main() {
    u := User{1, "Tom"}
    fmt.Printf("User: %p, %v\n", &u, u)
    mv := User.TestValue
    mv(u)
    mp := (*User).TestPointer
    mp(&u)
    mp2 := (*User).TestValue // *User ⽅方法集包含 TestValue。
    mp2(&u)                  // 签名变为 func TestValue(self *User)。
} // 实际依然是 receiver value copy
/*
User       : 0x210231000, {1 Tom}
TestValue  : 0x210231060, {1 Tom}
TestPointer: 0x210231000, &{1 Tom}
TestValue  : 0x2102310c0, {1 Tom}
*/
type Data struct{}

func (Data) TestValue()    {}
func (*Data) TestPointer() {}
func main() {
    var p *Data = nil
    p.TestPointer()
    (*Data)(nil).TestPointer() // method value
    (*Data).TestPointer(nil)   // method expression
    //p.TestValue()    // invalid memory address or nil pointer dereference
    // (Data)(nil).TestValue()  // cannot convert nil to type Data    
    // Data.TestValue(nil)      // cannot use nil as type Data in function argument 
}

简短变量声明左边的变量可能并不是全部都是刚刚声明的。如 果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过 的变量就只有赋值行为了,简短变量声明语句中必须至少要声明一个新的变量。如果变量 是在外部词法域声明的,那么简短变量声明语句将会在当前词法域重新声明一个新的变量

//交换两个元素
x, y = y, x
a[i], a[j] = a[j], a[i]
//获得整数对应二进制数1的个数
var pc [256]byte

func init() {
    for i := range pc {
        pc[i] = pc[i/2] + byte(i&1)
    }
    fmt.Printf("%b", pc)
}

func PopCount(x uint64) int {
    return int(pc[byte(x>>(0*8))] +
        pc[byte(x>>(1*8))] +
        pc[byte(x>>(2*8))] +
        pc[byte(x>>(3*8))] +
        pc[byte(x>>(4*8))] +
        pc[byte(x>>(5*8))] +
        pc[byte(x>>(6*8))] +
        pc[byte(x>>(7*8))])
}

表达式 x&(x-1) 用于将x的最低的一个非零的bit位清零。已用来计算二进制数1的个数

作用域

    if x := f(); x == 0 {
        fmt.Println(x)
    } else if y := g(x); x == y {
        fmt.Println(x, y)
    } else {
        fmt.Println(x, y)
    }
    fmt.Println(x, y) //    compile    error:    x    and    y    are    not    visible    here
var cwd string
func init() {
    cwd, err := os.Getwd() //    compile    error:    unused:    cwd        
    if err != nil {
        log.Fatalf("os.Getwd    failed:    %v", err)
    }
}

虽然cwd在外部已经声明过,但是 := 语句还是将cwd和err重新声明为新的局部变量。因为内 部声明的cwd将屏蔽外部的声明,因此上面的代码并不会正确更新包级声明的cwd变量。

最直接的方法是通过单独声明err变量,来避免使 用 := 的简短声明方式:

var cwd string
func init() {
    var err error
    cwd, err = os.Getwd()
    if err != nil {
        log.Fatalf("os.Getwd    failed:    %v", err)
    }
}

运算符号:

在Go语言中,%取模运算 符的符号和被取模数的符号总是一致的,因此 -5%3 和 -5%-3 结果都是-2。除法运算符 / 的 行为则依赖于操作数是否为全为整数,比如 5.0/4.0 的结果是1.25,但是5/4的结果是1,因为 整数除法会向着0方向截断余数。

 位运算:

^     位运算   XOR //只有一个1时返回1
&^    位清空   (AND NOT)

位操作运算符 ^ 作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反

 

结构

点操作符也可以和指向结构体的指针一起工作:

var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"

相当于

(*employeeOfTheMonth).Position += " (proactive team    player)"
dilbert.Position = "hello" 相当于 (&dilbert).Position = "hello2"

 

pp := &Point{1, 2}
相当于
pp := new(Point)
*pp = Point{1, 2}
w = Wheel{Circle{Point{8, 8}, 5}, 20}
等价于
w = Wheel{
    Circle: Circle{
        Point: Point{X:    8, Y: 8},
        Radius: 5,
    },    
    Spokes: 20,    //NOTE: trailing comma necessary here (and    at    Radius) 
}      

 

// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
    var x int
    return func() int {
        x++
        return x * x
    }
}
func main() {
    f := squares()
    fmt.Println(f()) // "1"
    fmt.Println(f()) // "4"
    fmt.Println(f()) // "9"
    fmt.Println(f()) // "16"
}

 

 

结构

var (
    mu sync.Mutex // guards mapping
    mapping = make(map[string]string)
)

func Lookup(key string) string {
    mu.Lock()
    v := mapping[key]
    mu.Unlock()
    return v
}

下面这个版本在功能上是一致的,但将两个包级别的变量放在了cache这个struct一组内:

var cache = struct {
    sync.Mutex
    mapping map[string]string
}{
    mapping: make(map[string]string),
}


func Lookup(key string) string {
    cache.Lock()
    v := cache.mapping[key]
    cache.Unlock()
    return v
}

 

 

type Point struct{ X, Y float64 }

func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} }
func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} }

type Path []Point

func (path Path) TranslateBy(offset Point, add bool) {
    var op func(p, q Point) Point
    if add {
        op = Point.Add
    } else {
        op = Point.Sub
    }
    for i := range path {
        // Call either path[i].Add(offset) or path[i].Sub(offset).
        path[i] = op(path[i], offset)
    }
}

 

 

deferred

 

func ma() {
    fmt.Println("im ma")
}

func main() {
    defer ma()
    fmt.Println("start")
}
/*
start
im ma
*/
func ma() func() {
    fmt.Println("im ma")
    return func() {
        fmt.Print("ma func end")
    }
}

func main() {
    defer ma()()
    fmt.Println("start")
}
/*
im ma
start
ma func end
*/

 

 

接口

type IntSet struct { /* ... */ }
func (*IntSet) String() string
var _ = IntSet{}.String() // compile error: String requires *IntSet receiver

但是我们可以在一个IntSet值上调用这个方法: var s IntSet var _ = s.String() // OK: s is a variable and &s has a String method

然而,由于只有*IntSet类型有String方法,所以也只有*IntSet类型实现了fmt.Stringer接口: var _ fmt.Stringer = &s // OK var _ fmt.Stringer = s // compile error: IntSet lacks String method

 

array := [5]*int{0: new(int), 1: new(int)}

*array[0] = 10
*array[1] = 20

 

创建一个包含 100 万个 int 类型元素的数组

var array [1e6]int

 

nil slice 和 empty slice 区别 https://blog.csdn.net/bobodem/article/details/80658466

nil slice

var slice []int

 

empty slice

slice := make([]int,0)//或者

slice := []int{}

 

  根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时,
总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复
制,并传递给函数。

  在函数间传递切片就是要在函数间以值的方式传递切片。由于切片的尺寸很小,在函数间复
制和传递切片成本也很低。由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片

复制到任意函数的时候,对底层数组大小都不会有影响。复制时只会复制切片本身,不会涉及底

层数组。

  在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对
这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。这个特性和切片类似,保证可以用很小的成本来复制映射。

 

 

func main() {
    slice := []int{10, 20, 30, 40, 50}
    newSliceA := slice[1:3:3]
    newSliceB := slice[1:3]
    newSliceA = append(newSliceA, 22)
    fmt.Println(slice)
    fmt.Println(newSliceA)
    newSliceB = append(newSliceB, 22)
    fmt.Println(slice)
    fmt.Println(newSliceA)
}
/*
[10 20 30 40 50]
[20 30 22]
[10 20 30 22 50]
[20 30 22]
 */

 

 

func main() {
    slice := make([]int ,1E6)
    fmt.Println(len(slice))
    fmt.Println(cap(slice))
    slice = append(slice, 99)
    fmt.Println(len(slice))
    fmt.Println(cap(slice))
    /**
1000000
1000000
1000001
1250304
     */
}

 

Go 语言里的引用类型有如下几个:切片、映射、通道、接口和函数类型

 

转载于:https://www.cnblogs.com/8000cabbage/p/8453815.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值