go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。

    1. package main
    2. func foo(arg_val int) (*int) {
    3. var foo_val1 int = 11;
    4. var foo_val2 int = 12;
    5. var foo_val3 int = 13;
    6. var foo_val4 int = 14;
    7. var foo_val5 int = 15;
    8. //此处循环是防止go编译器将foo优化成inline(内联函数)
    9. //如果是内联函数,main调用foo将是原地展开,所以foo_val1-5相当于main作用域的变量
    10. //即使foo_val3发生逃逸,地址与其他也是连续的
    11. for i := 0; i < 5; i++ {
    12. println(&arg_val, &foo_val1, &foo_val2, &foo_val3, &foo_val4, &foo_val5)
    13. }
    14. //返回foo_val3给main函数
    15. return &foo_val3;
    16. }
    17. func main() {
    18. main_val := foo(666)
    19. println(*main_val, main_val)
    20. }
    21. 输出:
    22. 0xc000037f58 0xc000037f38 0xc000037f30 0xc00000a028 0xc000037f28 0xc000037f20
    23. 0xc000037f58 0xc000037f38 0xc000037f30 0xc00000a028 0xc000037f28 0xc000037f20
    24. 0xc000037f58 0xc000037f38 0xc000037f30 0xc00000a028 0xc000037f28 0xc000037f20
    25. 0xc000037f58 0xc000037f38 0xc000037f30 0xc00000a028 0xc000037f28 0xc000037f20
    26. 0xc000037f58 0xc000037f38 0xc000037f30 0xc00000a028 0xc000037f28 0xc000037f20
    27. 13 0xc00000a028

    我们能看到foo_val3是返回给main的局部变量, 其中他的地址应该是0xc000082000,很明显与其他的foo_val1、2、3、4不是连续的.

    什么是内联函数
    简单理解就是编译时把函数的定义替换到调用的位置。

    1. inline int Add(int a, int b)
    2. {
    3. return a + b;
    4. }
    5. int main()
    6. {
    7. int num1 = 1;
    8. int num2 = 2;
    9. int myNum = Add(num1, num2);
    10. }
    11. //这样的代码内联之后大概就是
    12. int main()
    13. {
    14. int num1 = 1;
    15. int num2 = 2;
    16. int myNum = num1 + num2;
    17. }

    在内敛函数中foo_val1-5相当于main作用域的变量,即使foo_val3发生逃逸,地址与其他也是连续的

    1. package main
    2. func foo(arg_val int) (*int) {
    3. var foo_val1 int = 11;
    4. var foo_val2 int = 12;
    5. var foo_val3 int = 13;
    6. var foo_val4 int = 14;
    7. var foo_val5 int = 15;
    8. //此处循环是防止go编译器将foo优化成inline(内联函数)
    9. //如果是内联函数,main调用foo将是原地展开,所以foo_val1-5相当于main作用域的变量
    10. //即使foo_val3发生逃逸,地址与其他也是连续的
    11. println(&arg_val, &foo_val1, &foo_val2, &foo_val3, &foo_val4, &foo_val5)
    12. //返回foo_val3main函数
    13. return &foo_val3;
    14. }
    15. func main() {
    16. main_val := foo(666)
    17. println(*main_val, main_val)
    18. }
    19. 输出:
    20. 0xc000037f68 0xc000037f60 0xc000037f58 0xc000037f50 0xc000037f48 0xc000037f40
    21. 13 0xc000037f50