golang 学习笔记 - struct操作/切片/nil接口值

这篇博客总结了Go语言中struct操作的特性,包括结构体复制时的指针问题和匿名结构体的使用。同时,文章探讨了切片的陷阱,解释了在特定情况下修改切片值可能影响原数组的原因。最后,讨论了nil接口值的相关知识点。
摘要由CSDN通过智能技术生成

在看了文章Go 语言的优点,缺点和令人厌恶的设计之后,受益匪浅,觉得需要总结联系一下强化一下记忆。

struct操作

  • 复制

struct可以通过赋值语句来复制,如下:
虽然y是复制的x,但是修改y内array内容时候,会把x的也改变了。

import "fmt"

func main() {
	x := TestFo{Name: "foo1", Childs: []string{"foo1-2"}}

	y := x
	y.Name = "boo1"
	y.Childs[0] = "boo1-2"

	fmt.Printf("x is %v\n", x) //x is {foo1 [boo1-2]}
	fmt.Printf("y is %v\n", y) //y is {boo1 [boo1-2]}
}

type TestFo struct {
	Name   string
	Childs []string
}

这个例子就很清晰的描述了为什么会发上上面的情况,slice,map,array都是指针,复制struct时候,如果是指针只会复制指针,不会复制指针指向的值,可以看到,打印出来的两个指针是相同的,所以复制后的struct修改指针指向的值会影响被复制的struct。

func main() {
	x := TestFo{Name: "foo1", Child: &Child{Name: "foo1-2"}, Child2: Child{Name: "foo1-3"}}

	y := x
	y.Name = "boo1"
	y.Child.Name = "boo1-2"
	y.Child2.Name = "boo1-3"

	fmt.Printf("x is %v  x.Child is %v\n", x, x.Child) // x is {boo1 0xc42007a1b0 {foo1-3}}  y.Child is &{boo1-2}
	fmt.Printf("y is %v  y.Child is %v\n", y, y.Child) // y is {boo1 0xc42007a1b0 {boo1-3}}  y.Child is &{boo1-2}
}

type TestFo struct {
	Name  string
	Child *Child
	Child2 Child
}

type Child struct {
	Name string
}
  • 匿名struct
  1. 声明一个struct包含另一个struct时,可以匿名声明,匿名声明的好处是,可以隐式调用被包含的struct方法以及属性,如:
package main
type Car struct {
	Name string
}

func (c Car) Print() {
	println("this is "+c.Name)
}

type City struct {
	Car
}

func main() {
	c := City{}
	c.Name = "bmw"
	c.Print()
}
  1. 如果被包含的struct中属性有重名的,那么就没法隐式调用了,隐式调用其实应该是一块go的语法糖。
package main
type Car struct {
	Name string
}

type People struct {
	Name string
}

type City struct {
	Car
	People
}

func main() {
	c := City{}
	// c.Name = "car" // 报错:ambiguous selector c.Name
	c.Car.Name = "car"
	c.People.Name = "people"
}
  1. struct中包含指针型struct,那么就算是隐式调用,也得先初始化。
package main

type Car struct {
	Name string
}

type Build struct {
	BuildName string
}

type City struct {
	Build
	*Car
}

func main() {
	c := City{}
	// c.Name = "car" // 编译是可以通过的,但是运行时会在这里中断:bad
	println(fmt.Sprintln(c)) // {{}, <nil>} 这里面 {}是struct的初值,<nil>是指针类型的初值。
	c.Car = &Car{}
	c.Name = "car"
	c.BuildName = "big wall"
}

切片陷阱

对于[:]或[x:]这种切片进行append操作之后,修改切片内的值,要小心,如果原数组的cap(array) > len(array),而你append之后的长度有小于等于原数组的cap,那修改切片会影响到原数组,否则不会。
原因在于,array2 = append(array1, …)操作如果cap(array1) < len(array2),那么append会重新分配内存用以拼接,那array2的地址就是新地址,修改array2不会影响到array1,如果cap(array1) >= len(array2),那么array2的地址是包含在array1里的,修改array2[:len(array1)-1]的值,会影响到array1。具体例子如下:

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := s1[3:]
	s3 := append(s1[3:], 6)
	s3[0] = 7
	fmt.Printf("s1=%v  s2=%v  s3=%v\n", s1, s2, s3)  // s1=[1 2 3 4 5]  s2=[4 5]  s3=[7 5 6]

	ss1 := make([]int, 5, 10)
	for i := 1; i < 6; i++ {
		ss1[i-1] = i
	}
	ss2 := ss1[3:]
	ss3 := append(ss2, 6)
	ss3[0] = 7
	fmt.Printf("ss1=%v  ss2=%v  ss3=%v\n", ss1, ss2, ss3) // ss1=[1 2 3 7 5]  ss2=[7 5]  ss3=[7 5 6] ss1[3]和ss2[0]被改变了!
}

NIL接口值

package main

import "fmt"

func main() {
	var t *Test
	var f Foo = t
	if f != nil {
		println(t, f)                // 0x0 (0x114f580,0x0)
		fmt.Printf("%v  %v\n", t, f) // <nil>  <nil>
		f.Can()                      // Test Can
		f.Cannot()                   // panic: value method main.Test.Cannot called using nil *Test pointer
	}
}

type Foo interface {
	Can()
	Cannot()
}

type Test struct{}

func (*Test) Can()   { fmt.Println("Test Can") }
func (Test) Cannot() { fmt.Println("Test Cannot") }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值