Go语言学习——彻底弄懂return和defer的微妙关系

疑问

  前面在函数篇里介绍了Go语言的函数是支持多返回值的。

  只要在函数体内,对返回值赋值,最后加上return就可以返回所有的返回值。

  最近在写代码的时候经常遇到在return后,还要在defer里面做一些收尾工作,比如事务的提交或回滚。所以想弄清楚这个return和defer到底是什么关系,它们谁先谁后,对于最后返回值又有什么影响呢?

动手验证

  了解下来,问题比我想的要复杂,不信你先看看下面这段代码输出结果是啥

package main

import "fmt"

func main()  {
	fmt.Println("f1 result: ", f1())
	fmt.Println("f2 result: ", f2())
}

func f1() int {
	var i int
	defer func() {
		i++
		fmt.Println("f11: ", i)
	}()

	defer func() {
		i++
		fmt.Println("f12: ", i)
	}()

	i = 1000
	return i
}

func f2() (i int) {
	defer func() {
		i++
		fmt.Println("f21: ", i)
	}()

	defer func() {
		i++
		fmt.Println("f22: ", i)
	}()

	i = 1000
	return i
}

  

  最后的执行结果如下

f12: 1001
f11: 1002
f1 result: 1000
f22: 1001
f21: 1002
f2 result: 1002

  

f1函数:

  进入该函数,因为没有指定返回值变量,需要先声明i变量,因为是int类型,如果没有赋值,该变量初始化值为0,之后执行i=1000的赋值操作,然后执行return语句,返回i的值。

  真正返回之前还要执行defer函数部分,两个defer函数分别针对i进行自增操作,i的值依次为1001和1002

f2函数:

  进入该函数,因为已经定义好了返回值变量即为i,然后直接赋值i=1000,再返回i的值。

  同样的,也要在真正返回i前,执行两个defer函数,同样i依次自增得到1001和1002。

  问题的关键是为什么无名参数返回的值是1000,其并未收到defer函数对于i自增的影响;而有名函数在执行defer后,最后返回的i值为1002。

  网上找了一些原因,提到一个结论

原因就是return会将返回值先保存起来,对于无名返回值来说,
 保存在一个临时对象中,defer是看不到这个临时对象的;
 而对于有名返回值来说,就保存在已命名的变量中。

  看到这个结论,我想试试通过打印i的地址值是否可以看出一些端倪和线索

  为此在两个函数中添加了打印i的地址信息

package main

import "fmt"

func main()  {
	fmt.Println("f1 result: ", f1())
	fmt.Println("f2 result: ", f2())
}

func f1() int {
	var i int
	fmt.Printf("i: %p \n", &i)
	defer func() {
		i++
		fmt.Printf("i: %p \n", &i)
		fmt.Println("f11: ", i)
	}()

	defer func() {
		i++
		fmt.Printf("i: %p \n", &i)
		fmt.Println("f12: ", i)
	}()

	i = 1000
	return i
}

func f2() (i int) {
	fmt.Printf("i: %p \n", &i)
	defer func() {
		i++
		fmt.Printf("i: %p \n", &i)
		fmt.Println("f21: ", i)
	}()

	defer func() {
		i++
		fmt.Printf("i: %p \n", &i)
		fmt.Println("f22: ", i)
	}()
    i = 1000
	return i
}

  

  程序输出结果为

i: 0xc000090000
i: 0xc000090000
f12:  1001
i: 0xc000090000
f11:  1002
f1 result:  1000
i: 0xc00009a008
i: 0xc00009a008
f22:  1001
i: 0xc00009a008
f21:  1002
f2 result:  1002

  

  从这个结果可以看出,无论是f1还是f2函数中,变量i的地址全程没有改变过。

  所以对于上面这个结论我似乎懂了,但是还是有些模糊,return保存在一个临时对象中,defer看不到这个临时变量,但是i的值为什么能够在1000的基础上累加呢?

拨开云雾

  如果要从根本解决这个疑问,最好能够看看这段程序执行,背后的内存是如何分配的。

  这时候想到了前几天看书里提到的可以通过命令将go语言转为汇编语言。

  为了简化问题,将源代码修改为

package main

import "fmt"

func main()  {
	fmt.Println("f1 result: ", f1())
	fmt.Println("f2 result: ", f2())
}

func f1() int {
	var i int
	defer func() {
		i++
		fmt.Println("f11: ", i)
	}()

	i = 1000
	return i
}

func f2() (i int) {
	defer func() {
		i++
		fmt.Println("f21: ", i)
	}()
	i = 1000
	return i
}

  

  通过执行命令go tool compile -S test.go得到汇编代码如下

os.(*File).close STEXT dupok nosplit size=26 args=0x18 locals=0x0
	...
	0x0000 00000 (test.go:5)	TEXT	"".main(SB), ABIInternal, $136-0
	0x0000 00000 (test.go:5)	MOVQ	(TLS), CX
	0x0009 00009 (test.go:5)	LEAQ	-8(SP), AX
	0x000e 00014 (test.go:5)	CMPQ	AX, 16(CX)
	0x0012 00018 (test.go:5)	JLS	315
	0x0018 00024 (test.go:5)	SUBQ	$136, SP
	0x001f 00031 (test.go:5)	MOVQ	BP, 128(SP)
	0x0027 00039 (test.go:5)	LEAQ	128(SP), BP
	0x002f 00047 (test.go:5)	FUNCDATA	$0, gclocals·7d2d5fca80364273fb07d5820a76fef4(SB)
	...
"".f1 STEXT size=145 args=0x8 locals=0x28
	0x0000 00000 (test.go:10)	TEXT	"".f1(SB), ABIInternal, $40-8
	0x0000 00000 (test.go:10)	MOVQ	(TLS), CX
	0x0009 00009 (test.go:10)	CMPQ	SP, 16(CX)
	0x000d 00013 (test.go:10)	JLS	135
	0x000f 00015 (test.go:10)	SUBQ	$40, SP
	0x0013 00019 (test.go:10)	MOVQ	BP, 32(SP)
	0x0018 00024 (test.go:10)	LEAQ	32(SP), BP
	0x001d 00029 (test.go:10)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:10)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:10)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x001d 00029 (test.go:10)	PCDATA	$2, $0
	0x001d 00029 (test.go:10)	PCDATA	$0, $0
	0x001d 00029 (test.go:10)	MOVQ	$0, "".~r0+48(SP)
	0x0026 00038 (test.go:11)	MOVQ	$0, "".i+24(SP)
	0x002f 00047 (test.go:12)	MOVL	$8, (SP)
	0x0036 00054 (test.go:12)	PCDATA	$2, $1
	0x0036 00054 (test.go:12)	LEAQ	"".f1.func1·f(SB), AX
	0x003d 00061 (test.go:12)	PCDATA	$2, $0
	0x003d 00061 (test.go:12)	MOVQ	AX, 8(SP)
	0x0042 00066 (test.go:12)	PCDATA	$2, $1
	0x0042 00066 (test.go:12)	LEAQ	"".i+24(SP), AX
	0x0047 00071 (test.go:12)	PCDATA	$2, $0
	0x0047 00071 (test.go:12)	MOVQ	AX, 16(SP)
	0x004c 00076 (test.go:12)	CALL	runtime.deferproc(SB)
	0x0051 00081 (test.go:12)	TESTL	AX, AX
	0x0053 00083 (test.go:12)	JNE	119
	0x0055 00085 (test.go:17)	MOVQ	$1000, "".i+24(SP)
	0x005e 00094 (test.go:18)	MOVQ	$1000, "".~r0+48(SP)
	0x0067 00103 (test.go:18)	XCHGL	AX, AX
	0x0068 00104 (test.go:18)	CALL	runtime.deferreturn(SB)
	0x006d 00109 (test.go:18)	MOVQ	32(SP), BP
	0x0072 00114 (test.go:18)	ADDQ	$40, SP
	0x0076 00118 (test.go:18)	RET
	0x0077 00119 (test.go:12)	XCHGL	AX, AX
	0x0078 00120 (test.go:12)	CALL	runtime.deferreturn(SB)
	0x007d 00125 (test.go:12)	MOVQ	32(SP), BP
	0x0082 00130 (test.go:12)	ADDQ	$40, SP
	0x0086 00134 (test.go:12)	RET
	0x0087 00135 (test.go:12)	NOP
	0x0087 00135 (test.go:10)	PCDATA	$0, $-1
	0x0087 00135 (test.go:10)	PCDATA	$2, $-1
	0x0087 00135 (test.go:10)	CALL	runtime.morestack_noctxt(SB)
	0x008c 00140 (test.go:10)	JMP	0
	...
	0x0000 00000 (test.go:21)	TEXT	"".f2(SB), ABIInternal, $32-8
	0x0000 00000 (test.go:21)	MOVQ	(TLS), CX
	0x0009 00009 (test.go:21)	CMPQ	SP, 16(CX)
	0x000d 00013 (test.go:21)	JLS	117
	0x000f 00015 (test.go:21)	SUBQ	$32, SP
	0x0013 00019 (test.go:21)	MOVQ	BP, 24(SP)
	0x0018 00024 (test.go:21)	LEAQ	24(SP), BP
	0x001d 00029 (test.go:21)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:21)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:21)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x001d 00029 (test.go:21)	PCDATA	$2, $0
	0x001d 00029 (test.go:21)	PCDATA	$0, $0
	0x001d 00029 (test.go:21)	MOVQ	$0, "".i+40(SP)
	0x0026 00038 (test.go:22)	MOVL	$8, (SP)
	0x002d 00045 (test.go:22)	PCDATA	$2, $1
	0x002d 00045 (test.go:22)	LEAQ	"".f2.func1·f(SB), AX
	0x0034 00052 (test.go:22)	PCDATA	$2, $0
	0x0034 00052 (test.go:22)	MOVQ	AX, 8(SP)
	0x0039 00057 (test.go:22)	PCDATA	$2, $1
	0x0039 00057 (test.go:22)	LEAQ	"".i+40(SP), AX
	0x003e 00062 (test.go:22)	PCDATA	$2, $0
	0x003e 00062 (test.go:22)	MOVQ	AX, 16(SP)
	0x0043 00067 (test.go:22)	CALL	runtime.deferproc(SB)
	0x0048 00072 (test.go:22)	TESTL	AX, AX
	0x004a 00074 (test.go:22)	JNE	101
	0x004c 00076 (test.go:26)	MOVQ	$1000, "".i+40(SP)
	0x0055 00085 (test.go:27)	XCHGL	AX, AX
	0x0056 00086 (test.go:27)	CALL	runtime.deferreturn(SB)
	0x005b 00091 (test.go:27)	MOVQ	24(SP), BP
	0x0060 00096 (test.go:27)	ADDQ	$32, SP
	0x0064 00100 (test.go:27)	RET
	0x0065 00101 (test.go:22)	XCHGL	AX, AX
	0x0066 00102 (test.go:22)	CALL	runtime.deferreturn(SB)
	0x006b 00107 (test.go:22)	MOVQ	24(SP), BP
	0x0070 00112 (test.go:22)	ADDQ	$32, SP
	0x0074 00116 (test.go:22)	RET
	0x0075 00117 (test.go:22)	NOP
	0x0075 00117 (test.go:21)	PCDATA	$0, $-1
	0x0075 00117 (test.go:21)	PCDATA	$2, $-1
	0x0075 00117 (test.go:21)	CALL	runtime.morestack_noctxt(SB)
	0x007a 00122 (test.go:21)	JMP	0
	...                    ........
	rel 16+8 t=1 type.[2]interface {}+0

  

  感觉离真相只差一步了,就是看完这段汇编代码就能搞明白这个return在无名和有名返回值时分别做了什么,所谓的零时变量是咋分配的,想想就有点小激动呢

  但是,比较棘手的是,我没学过汇编-_-!

  但是again,这有什么关系呢,两个函数既然执行结果不一样,那么在汇编层面肯定也有不一样的地方,于是开始找不同,最终在上面的汇编代码分别找到关键信息如下

"".f2 STEXT size=124 args=0x8 locals=0x20
	0x0000 00000 (test.go:21)	TEXT	"".f2(SB), ABIInternal, $32-8
	0x0000 00000 (test.go:21)	MOVQ	(TLS), CX
	0x0009 00009 (test.go:21)	CMPQ	SP, 16(CX)
	0x000d 00013 (test.go:21)	JLS	117
	0x000f 00015 (test.go:21)	SUBQ	$32, SP
	0x0013 00019 (test.go:21)	MOVQ	BP, 24(SP)
	0x0018 00024 (test.go:21)	LEAQ	24(SP), BP
	0x001d 00029 (test.go:21)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:21)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:21)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x001d 00029 (test.go:21)	PCDATA	$2, $0
	0x001d 00029 (test.go:21)	PCDATA	$0, $0
	0x001d 00029 (test.go:21)	MOVQ	$0, "".i+40(SP)
	0x0026 00038 (test.go:22)	MOVL	$8, (SP)
	0x002d 00045 (test.go:22)	PCDATA	$2, $1
	0x002d 00045 (test.go:22)	LEAQ	"".f2.func1·f(SB), AX
	0x0034 00052 (test.go:22)	PCDATA	$2, $0
	0x0034 00052 (test.go:22)	MOVQ	AX, 8(SP)
	0x0039 00057 (test.go:22)	PCDATA	$2, $1
	0x0039 00057 (test.go:22)	LEAQ	"".i+40(SP), AX
	0x003e 00062 (test.go:22)	PCDATA	$2, $0
	0x003e 00062 (test.go:22)	MOVQ	AX, 16(SP)
	0x0043 00067 (test.go:22)	CALL	runtime.deferproc(SB)
	0x0048 00072 (test.go:22)	TESTL	AX, AX
	0x004a 00074 (test.go:22)	JNE	101
	0x004c 00076 (test.go:26)	MOVQ	$1000, "".i+40(SP)
	0x0055 00085 (test.go:27)	XCHGL	AX, AX
	0x0056 00086 (test.go:27)	CALL	runtime.deferreturn(SB)
	0x005b 00091 (test.go:27)	MOVQ	24(SP), BP
	0x0060 00096 (test.go:27)	ADDQ	$32, SP
	0x0064 00100 (test.go:27)	RET
	0x0065 00101 (test.go:22)	XCHGL	AX, AX
	0x0066 00102 (test.go:22)	CALL	runtime.deferreturn(SB)
	0x006b 00107 (test.go:22)	MOVQ	24(SP), BP
	0x0070 00112 (test.go:22)	ADDQ	$32, SP
	0x0074 00116 (test.go:22)	RET
	0x0075 00117 (test.go:22)	NOP
	0x0075 00117 (test.go:21)	PCDATA	$0, $-1
	0x0075 00117 (test.go:21)	PCDATA	$2, $-1
	0x0075 00117 (test.go:21)	CALL	runtime.morestack_noctxt(SB)
	0x007a 00122 (test.go:21)	JMP	0

  

  这是f2有名返回值的关键信息,主要看

0x004c 00076 (test.go:26)	MOVQ	$1000, "".i+40(SP)

  这个大概意思就是把1000放到"".i+40(SP)这个内存地址上,然后下面执行的操作就是返回了

"".f1 STEXT size=145 args=0x8 locals=0x28
	0x0000 00000 (test.go:10)	TEXT	"".f1(SB), ABIInternal, $40-8
	0x0000 00000 (test.go:10)	MOVQ	(TLS), CX
	0x0009 00009 (test.go:10)	CMPQ	SP, 16(CX)
	0x000d 00013 (test.go:10)	JLS	135
	0x000f 00015 (test.go:10)	SUBQ	$40, SP
	0x0013 00019 (test.go:10)	MOVQ	BP, 32(SP)
	0x0018 00024 (test.go:10)	LEAQ	32(SP), BP
	0x001d 00029 (test.go:10)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:10)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (test.go:10)	FUNCDATA	$3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
	0x001d 00029 (test.go:10)	PCDATA	$2, $0
	0x001d 00029 (test.go:10)	PCDATA	$0, $0
	0x001d 00029 (test.go:10)	MOVQ	$0, "".~r0+48(SP)
	0x0026 00038 (test.go:11)	MOVQ	$0, "".i+24(SP)
	0x002f 00047 (test.go:12)	MOVL	$8, (SP)
	0x0036 00054 (test.go:12)	PCDATA	$2, $1
	0x0036 00054 (test.go:12)	LEAQ	"".f1.func1·f(SB), AX
	0x003d 00061 (test.go:12)	PCDATA	$2, $0
	0x003d 00061 (test.go:12)	MOVQ	AX, 8(SP)
	0x0042 00066 (test.go:12)	PCDATA	$2, $1
	0x0042 00066 (test.go:12)	LEAQ	"".i+24(SP), AX
	0x0047 00071 (test.go:12)	PCDATA	$2, $0
	0x0047 00071 (test.go:12)	MOVQ	AX, 16(SP)
	0x004c 00076 (test.go:12)	CALL	runtime.deferproc(SB)
	0x0051 00081 (test.go:12)	TESTL	AX, AX
	0x0053 00083 (test.go:12)	JNE	119
	0x0055 00085 (test.go:17)	MOVQ	$1000, "".i+24(SP)
	0x005e 00094 (test.go:18)	MOVQ	$1000, "".~r0+48(SP)
	0x0067 00103 (test.go:18)	XCHGL	AX, AX
	0x0068 00104 (test.go:18)	CALL	runtime.deferreturn(SB)
	0x006d 00109 (test.go:18)	MOVQ	32(SP), BP
	0x0072 00114 (test.go:18)	ADDQ	$40, SP
	0x0076 00118 (test.go:18)	RET
	0x0077 00119 (test.go:12)	XCHGL	AX, AX
	0x0078 00120 (test.go:12)	CALL	runtime.deferreturn(SB)
	0x007d 00125 (test.go:12)	MOVQ	32(SP), BP
	0x0082 00130 (test.go:12)	ADDQ	$40, SP
	0x0086 00134 (test.go:12)	RET
	0x0087 00135 (test.go:12)	NOP
	0x0087 00135 (test.go:10)	PCDATA	$0, $-1
	0x0087 00135 (test.go:10)	PCDATA	$2, $-1
	0x0087 00135 (test.go:10)	CALL	runtime.morestack_noctxt(SB)
	0x008c 00140 (test.go:10)	JMP	0

  

这是f1无名返回值的关键信息,主要看

0x0055 00085 (test.go:17)	MOVQ	$1000, "".i+24(SP)
0x005e 00094 (test.go:18)	MOVQ	$1000, "".~r0+48(SP)

  这个大概意思就是把1000放到"".i+24(SP)这个内存地址上,然后又把1000赋给了"".~r0+48(SP),这就是和f1不一样的地方。对应前面结论,我们在这里找到了验证。大致过程就是无名返回值的情况,在return的时候开辟了一个新内存空间,后续的defer读取的还是"".i+24(SP)这样的内存地址而无法读取临时空间的值。return在函数最后返回的也是"".~r0+48(SP)对应的值即1000。(因为没有研究过汇编,有些细节可能有待考证)

结论

到此,我们算是搞明白了Go语言里面return和defer之间的微妙关系,从汇编层面看清了在无名返回值和有名返回值return返回的差异。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

原文地址:https://www.cnblogs.com/bigdataZJ/p/return-and-defer.html

时间: 2024-10-13 10:23:35

Go语言学习——彻底弄懂return和defer的微妙关系的相关文章

Java学习——JSTL标签与EL表达式之间的微妙关系

原文总结的太好了,忍不住记录.转发. 原文地址:http://blog.csdn.net/u010168160/article/details/49182867 目录(?)[-] 一EL表达式 EL相关概念 EL基本格式 EL语法组成-标识符 PS使用EL的时候默认会以一定顺序pageContextrequestsessionapplication搜索四个作用域将最先找到的变量值显示出来 开启和关闭EL表达式 二JSTL标签库 相关概念 JSTL标签库分类 JSTL的优点 为什么要用JSTL 使

必须弄懂的495个C语言问题

必须弄懂的495个C语言问题 1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于?32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外, 就使用int 型.如果严格定义的溢出特征很重要而负值无关紧要, 或者你希望在操作二进制位和字节时避免符号扩展的问题, 请使用对应的无符号类型.但是, 要注意在表达式中混用有符号和无符号值的情况. 尽管字符类型(尤其是无符号字符型) 可以当成"小" 整型使用

学习PS必须弄懂的专业术语

在学习PS的过程中,我们经常会遇到一些专业术语,下面我们来对一些常用的.比较难理解的术语进行简单讲解. 像素:像素是构成图像的最基本元素,它实际上是一个个独立的小方格,每个像素都能记录它所在的位置和颜色信息.下图中每一个小方格就是一个像素点,它记载着图像的各种信息. 选区:也叫选取范围,是PS对图像做编辑的范围,任何编辑对选区外无效.当图像上没有建立选择区时,相当于全部选择.下图中的黑白相间的细线就是选择区的边界,对图像的操作只对选择区内有效. 羽化:对选择区的边缘做软化处理,其对图像的编辑在选

关于c语言学习 谭浩强的书

2007-11-16 13:22:58|  分类: PROGRAMME |  标签: |举报 |字号大中小 订阅 广大有志于从事IT行业的同志们,在你们进入这一行之前千万请看这篇文章!太经典了!对你绝对有启发! 千万别买谭浩强和等级考试的书!!!!!! 整理别人的言论,请大家踊跃讨论!!!!!!!!!!!! 1:书皮上面有那么多的牛人题词,估计也许是自己的水平太低. 2:ANSI只给了两种方式:int main(void) {/*...*/}和 int main(int argc, char *

【CodeForces】343D Water tree (线段树好题!还未弄懂)

/* 此题的方法除了用线段树求子树,通过标记父亲,更新儿子的方法,来更新祖先,学习了. 对于建树的方法由于并没有说明父亲与儿子的顺序,所以需要通过两次添加. 并且pre变量可以获得父亲的位置,还未弄懂! */ #define _CRT_SECURE_NO_WARNINGS #include<cstring> #include<cstdio> #include<iostream> #include<algorithm> using namespace std;

c语言学习之二

记得复读的时候,数学老师就经常给我们说,每天让自己轻松一点,不要给自己找那么多的麻烦……他不是让我们什么都不干,而是每天积累一点点,弄懂以前不明白的问题,让自己的生活过得轻松一点.我是十分认可这句话的,不管你学什么,都要扎扎实实的弄懂一点,不要贪多,贪多反而让你消化不了,反而会让你迷惑重重.学习任何东西,我们一定要知其所以然……懂得原理.就像以前老师为什么总是让我们去演算书上帮我们总结好的结论. 废话不多少,今天我们说一下C语言的常量和变量. 常量就是不能改变的量. 定义一个常量有两中方式: ①

【转】朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)

原文网址:http://bbs.elecfans.com/jishu_354666_1_1.html 再过1个月又是一年应届毕业生应聘的高峰期了,为了方便应届毕业生应聘,笔者将大学四年C语言知识及去年本人C语言笔试难点进行梳理,希望能对今年应届毕业生的应聘有所帮助. 2013年10月18日更新-->    攻破C语言这个帖子更新到这里,我不仅仅是为了补充大学学生遗漏的知识,我更重要的是希望通过我的经验,你们实际项目中的C语言写得漂亮,写出属于你的风格.“朱兆祺STM32手记”(http://bb

跨语言学习的基本思路及python的基础学习

笔者是C#出身,大学四年主修C#,工作三年也是C#语言开发.但在学校里其他的语言也有相应的课程,eg:Java,Php,C++都学过,当然只是学了皮毛(大学嘛,你懂得),严格来说未必入门,但这些语言的代码阅读倒是不成问题,毕竟触类旁通嘛,有道是“一法通,万法明”,多学学其他的也没坏处. 近期被临时借调到其他项目组,由于新项目用的是Python,本人呢又没有python开发经验(之前只是知道有这门语言),工期呢两周多吧,简单来说就是时间紧,任务重,而且开发环境完全陌生,于是就得快速学会使用一门新语

彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js是一行一行执行的?还用你说?稍安勿躁,正因为js是一行一行执行的,所以我们以为js都是这样的: let a