func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
返回结果为
entering: b
in b
entering: a
in a
leaving: a
leaving: b
这一特性可以用来设计系统状态转移机制。var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
// Unnecessarily complex:
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// Idiomatic:
v := make([]int, 100)
所以,对于值类型与引用类型,请分别使用普通定义方式和make方式,new不推荐。
示例:
type SV struct {
s []int
v int
}
func testModify(target SV) {
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.s[1] = 2
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.v = 10
}
func main() {
ts := make([]int, 2)
ts[0] = 1
target := SV{s: ts}
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
testModify(target)
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
}
结果:
target:main.SV{s:[]int{1, 0}, v:0}, &target:0xc00007e020, target.s:0xc000088010, target.s:[]int{1, 0}
target:main.SV{s:[]int{1, 0}, v:0}, &target:0xc00007e0a0, target.s:0xc000088010, target.s:[]int{1, 0}
target:main.SV{s:[]int{1, 2}, v:0}, &target:0xc00007e0a0, target.s:0xc000088010, target.s:[]int{1, 2}
target:main.SV{s:[]int{1, 2}, v:0}, &target:0xc00007e020, target.s:0xc000088010, target.s:[]int{1, 2}
但是这里也有两个坑。首先,如果源结构体的slice长度设成0,容量设为2,然后在函数中对其进行append操作,如下所示:
type SV struct {
s []int
v int
}
func testModify(target SV) {
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.s = append(target.s, 3)
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.v = 10
}
func main() {
ts := make([]int, 0, 2)
target := SV{s: ts}
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
testModify(target)
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
}
target:main.SV{s:[]int{}, v:0}, &target:0xc00000c080, target.s:0xc00001c110, target.s:[]int{}
target:main.SV{s:[]int{}, v:0}, &target:0xc00000c100, target.s:0xc00001c110, target.s:[]int{}
target:main.SV{s:[]int{3}, v:0}, &target:0xc00000c100, target.s:0xc00001c110, target.s:[]int{3}
target:main.SV{s:[]int{}, v:0}, &target:0xc00000c080, target.s:0xc00001c110, target.s:[]int{}
可以看到这个结果最后原结构体显示并没有append成功,明明ts的容量是足够append的,这里推测是原来的slice的长度属性没有被改变?但是看地址显示复制的结构体中的slice都是一个地址,如果函数中改变了长度应该会影响原来的结果才对,暂时比较迷惑这个点,希望有大神解答。第二个坑就比较常见了,append超出原slice的容量,导致slice新开内存扩容然后复制原slice的值。
type SV struct {
s []int
v int
}
func testModify(target SV) {
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.s = append(target.s, 3)
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
target.v = 10
}
func main() {
ts := make([]int, 2)
target := SV{s: ts}
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
testModify(target)
fmt.Printf("target:%#v, &target:%p, target.s:%p, target.s:%#v\n", target, &target, target.s, target.s)
}
target:main.SV{s:[]int{0, 0}, v:0}, &target:0xc00000c080, target.s:0xc00001c110, target.s:[]int{0, 0}
target:main.SV{s:[]int{0, 0}, v:0}, &target:0xc00000c100, target.s:0xc00001c110, target.s:[]int{0, 0}
target:main.SV{s:[]int{0, 0, 3}, v:0}, &target:0xc00000c100, target.s:0xc000016440, target.s:[]int{0, 0, 3}
target:main.SV{s:[]int{0, 0}, v:0}, &target:0xc00000c080, target.s:0xc00001c110, target.s:[]int{0, 0}
这里可以看到append后的slice内存地址已经变了,所以必然不会影响原slice。
GO中的数据格式转换,hex string to int
go的strconv包提供了足够的格式转换功能,但是对于hex string类型的负数值,直接使用ParseInt是会出问题的,见下面代码:func main() {
hexStr := "80000000"
num1, err := strconv.ParseInt(hexStr, 16, 32)
fmt.Printf("Original hexString:%s; Direct parseInt:%d; err:%s\n", hexStr, num1, err)
num2, err := strconv.ParseUint(hexStr, 16, 32)
realNum := int32(num2)
fmt.Printf("Original hexString:%s; ParseUint then transform:%d; err:%s\n", hexStr, realNum, err)
decStr := "-2147483648"
num3, err := strconv.ParseInt(decStr, 10, 32)
fmt.Printf("Original decString:%s; Direct parseInt:%d; err:%s\n", decStr, num3, err)
}
Original hexString:80000000; Direct parseInt:2147483647; err:strconv.ParseInt: parsing "80000000": value out of range
Original hexString:80000000; ParseUint then transform:-2147483648; err:%!s()
Original decString:-2147483649; Direct parseInt:-2147483648; err:%!s()
这里hexStr是负数,可以看到直接用ParseInt转是会溢出的,所以要先用ParseUint转uint32,然后显示转换成int32;而如果字符串是十进制形式的,就没这个问题,可以直接转。所以需要研究下ParseInt的源码,如下:
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
const fnParseInt = "ParseInt"
// Empty string bad.
if len(s) == 0 {
return 0, syntaxError(fnParseInt, s)
}
// Pick off leading sign.
s0 := s
neg := false
if s[0] == '+' {
s = s[1:]
} else if s[0] == '-' {
neg = true
s = s[1:]
}
// Convert unsigned and check range.
var un uint64
un, err = ParseUint(s, base, bitSize)
if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Func = fnParseInt
err.(*NumError).Num = s0
return 0, err
}
if bitSize == 0 {
bitSize = int(IntSize)
}
cutoff := uint64(1 << uint(bitSize-1))
if !neg && un >= cutoff {
return int64(cutoff - 1), rangeError(fnParseInt, s0)
}
if neg && un > cutoff {
return -int64(cutoff), rangeError(fnParseInt, s0)
}
n := int64(un)
if neg {
n = -n
}
return n, nil
}
可以看到,ParseInt在处理正负数时,判断标准仅仅是前面有没有带负号,而对于其他包括二进制、八进制、十六进制,首位最高位为1代表负数的形式,是不支持的。判断完符号,就调用ParseUint进行处理,得到结果会根据符号和bitSize来判断是否溢出,如果溢出就返回能表示的最大值以及错误信息。
type T struct {
a int
b float64
c string
}
t := &T{ 7, -2.35, "abc\tdef" }
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
&{7 -2.35 abc def}
&{a:7 b:-2.35 c:abc def}
&main.T{a:7, b:-2.35, c:"abc\tdef"}
可以看到转义字符不会生效。