本章实验是说明深度递归要占用大量内存空间,需谨慎使用。
实验代码如下:
/* Example of deep recursion */
#include <stdio.h>
#include <stdlib.h>
int recurse(int x) {
int a[1<<15]; /* 4 * 2^15 = 64 KiB */
printf("x = %d. a at %p\n", x, a);
a[0] = (1<<14)-1;
a[a[0]] = x-1;
if (a[a[0]] == 0)
return -1;
return recurse(a[a[0]]) - 1;
}
int main(int argc, char *argv[]) {
int x = 100;
if (argc > 1)
x = atoi(argv[1]);
int v = recurse(x);
printf("x = %d. recurse(x) = %d\n", x, v);
return 0;
}
以上代码部分是在每次递归过程中分配了2^15*4大小的空间给数组,占用如此大的空间再进行深度递归调用,我们就会发现机器的内存空间明显捉襟见肘了。所以递归函数要慎重使用。
接下来是无输入时取实验参数100时输出结果:
zhaoxiaoan@zhaoxiaoan:/mnt/hgfs/CS2$ ./runaway
x = 100. a at 0x7ffca9f53660
x = 99. a at 0x7ffca9f33630
x = 98. a at 0x7ffca9f13600
x = 97. a at 0x7ffca9ef35d0
x = 96. a at 0x7ffca9ed35a0
x = 95. a at 0x7ffca9eb3570
x = 94. a at 0x7ffca9e93540
x = 93. a at 0x7ffca9e73510
x = 92. a at 0x7ffca9e534e0
x = 91. a at 0x7ffca9e334b0
x = 90. a at 0x7ffca9e13480
x = 89. a at 0x7ffca9df3450
x = 88. a at 0x7ffca9dd3420
x = 87. a at 0x7ffca9db33f0
x = 86. a at 0x7ffca9d933c0
x = 85. a at 0x7ffca9d73390
x = 84. a at 0x7ffca9d53360
x = 83. a at 0x7ffca9d33330
x = 82. a at 0x7ffca9d13300
x = 81. a at 0x7ffca9cf32d0
x = 80. a at 0x7ffca9cd32a0
x = 79. a at 0x7ffca9cb3270
x = 78. a at 0x7ffca9c93240
x = 77. a at 0x7ffca9c73210
x = 76. a at 0x7ffca9c531e0
x = 75. a at 0x7ffca9c331b0
x = 74. a at 0x7ffca9c13180
x = 73. a at 0x7ffca9bf3150
x = 72. a at 0x7ffca9bd3120
x = 71. a at 0x7ffca9bb30f0
x = 70. a at 0x7ffca9b930c0
x = 69. a at 0x7ffca9b73090
x = 68. a at 0x7ffca9b53060
x = 67. a at 0x7ffca9b33030
x = 66. a at 0x7ffca9b13000
x = 65. a at 0x7ffca9af2fd0
x = 64. a at 0x7ffca9ad2fa0
x = 63. a at 0x7ffca9ab2f70
x = 62. a at 0x7ffca9a92f40
x = 61. a at 0x7ffca9a72f10
x = 60. a at 0x7ffca9a52ee0
x = 59. a at 0x7ffca9a32eb0
x = 58. a at 0x7ffca9a12e80
x = 57. a at 0x7ffca99f2e50
x = 56. a at 0x7ffca99d2e20
x = 55. a at 0x7ffca99b2df0
x = 54. a at 0x7ffca9992dc0
x = 53. a at 0x7ffca9972d90
x = 52. a at 0x7ffca9952d60
x = 51. a at 0x7ffca9932d30
x = 50. a at 0x7ffca9912d00
x = 49. a at 0x7ffca98f2cd0
x = 48. a at 0x7ffca98d2ca0
x = 47. a at 0x7ffca98b2c70
x = 46. a at 0x7ffca9892c40
x = 45. a at 0x7ffca9872c10
x = 44. a at 0x7ffca9852be0
x = 43. a at 0x7ffca9832bb0
x = 42. a at 0x7ffca9812b80
x = 41. a at 0x7ffca97f2b50
x = 40. a at 0x7ffca97d2b20
x = 39. a at 0x7ffca97b2af0
x = 38. a at 0x7ffca9792ac0
段错误 (核心已转储)
如上所示,我们知道机器的运行内存中空间是有限的,如果使用递归很容易发生严重的错误!
以下是该代码的汇编显示:
/*
zhaoxiaoan@zhaoxiaoan:/mnt/hgfs/CS2$ objdump -d runaway
runaway: 文件格式 elf64-x86-64
Disassembly of section .init:
0000000000000580 <_init>:
580: 48 83 ec 08 sub $0x8,%rsp
584: 48 8b 05 5d 0a 20 00 mov 0x200a5d(%rip),%rax # 200fe8 <__gmon_start__>
58b: 48 85 c0 test %rax,%rax
58e: 74 02 je 592 <_init+0x12>
590: ff d0 callq *%rax
592: 48 83 c4 08 add $0x8,%rsp
596: c3 retq
Disassembly of section .plt:
00000000000005a0 <.plt>:
5a0: ff 35 0a 0a 20 00 pushq 0x200a0a(%rip) # 200fb0 <_GLOBAL_OFFSET_TABLE_+0x8>
5a6: ff 25 0c 0a 20 00 jmpq *0x200a0c(%rip) # 200fb8 <_GLOBAL_OFFSET_TABLE_+0x10>
5ac: 0f 1f 40 00 nopl 0x0(%rax)
00000000000005b0 <__stack_chk_fail@plt>:
5b0: ff 25 0a 0a 20 00 jmpq *0x200a0a(%rip) # 200fc0 <__stack_chk_fail@GLIBC_2.4>
5b6: 68 00 00 00 00 pushq $0x0
5bb: e9 e0 ff ff ff jmpq 5a0 <.plt>
00000000000005c0 <printf@plt>:
5c0: ff 25 02 0a 20 00 jmpq *0x200a02(%rip) # 200fc8 <printf@GLIBC_2.2.5>
5c6: 68 01 00 00 00 pushq $0x1
5cb: e9 d0 ff ff ff jmpq 5a0 <.plt>
00000000000005d0 <atoi@plt>:
5d0: ff 25 fa 09 20 00 jmpq *0x2009fa(%rip) # 200fd0 <atoi@GLIBC_2.2.5>
5d6: 68 02 00 00 00 pushq $0x2
5db: e9 c0 ff ff ff jmpq 5a0 <.plt>
Disassembly of section .plt.got:
00000000000005e0 <__cxa_finalize@plt>:
5e0: ff 25 12 0a 20 00 jmpq *0x200a12(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
5e6: 66 90 xchg %ax,%ax
Disassembly of section .text:
00000000000005f0 <_start>:
5f0: 31 ed xor %ebp,%ebp
5f2: 49 89 d1 mov %rdx,%r9
5f5: 5e pop %rsi
5f6: 48 89 e2 mov %rsp,%rdx
5f9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
5fd: 50 push %rax
5fe: 54 push %rsp
5ff: 4c 8d 05 7a 02 00 00 lea 0x27a(%rip),%r8 # 880 <__libc_csu_fini>
606: 48 8d 0d 03 02 00 00 lea 0x203(%rip),%rcx # 810 <__libc_csu_init>
60d: 48 8d 3d 91 01 00 00 lea 0x191(%rip),%rdi # 7a5 <main>
614: ff 15 c6 09 20 00 callq *0x2009c6(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5>
61a: f4 hlt
61b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000000620 <deregister_tm_clones>:
620: 48 8d 3d e9 09 20 00 lea 0x2009e9(%rip),%rdi # 201010 <__TMC_END__>
627: 55 push %rbp
628: 48 8d 05 e1 09 20 00 lea 0x2009e1(%rip),%rax # 201010 <__TMC_END__>
62f: 48 39 f8 cmp %rdi,%rax
632: 48 89 e5 mov %rsp,%rbp
635: 74 19 je 650 <deregister_tm_clones+0x30>
637: 48 8b 05 9a 09 20 00 mov 0x20099a(%rip),%rax # 200fd8 <_ITM_deregisterTMCloneTable>
63e: 48 85 c0 test %rax,%rax
641: 74 0d je 650 <deregister_tm_clones+0x30>
643: 5d pop %rbp
644: ff e0 jmpq *%rax
646: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
64d: 00 00 00
650: 5d pop %rbp
651: c3 retq
652: 0f 1f 40 00 nopl 0x0(%rax)
656: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
65d: 00 00 00
0000000000000660 <register_tm_clones>:
660: 48 8d 3d a9 09 20 00 lea 0x2009a9(%rip),%rdi # 201010 <__TMC_END__>
667: 48 8d 35 a2 09 20 00 lea 0x2009a2(%rip),%rsi # 201010 <__TMC_END__>
66e: 55 push %rbp
66f: 48 29 fe sub %rdi,%rsi
672: 48 89 e5 mov %rsp,%rbp
675: 48 c1 fe 03 sar $0x3,%rsi
679: 48 89 f0 mov %rsi,%rax
67c: 48 c1 e8 3f shr $0x3f,%rax
680: 48 01 c6 add %rax,%rsi
683: 48 d1 fe sar %rsi
686: 74 18 je 6a0 <register_tm_clones+0x40>
688: 48 8b 05 61 09 20 00 mov 0x200961(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable>
68f: 48 85 c0 test %rax,%rax
692: 74 0c je 6a0 <register_tm_clones+0x40>
694: 5d pop %rbp
695: ff e0 jmpq *%rax
697: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
69e: 00 00
6a0: 5d pop %rbp
6a1: c3 retq
6a2: 0f 1f 40 00 nopl 0x0(%rax)
6a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6ad: 00 00 00
00000000000006b0 <__do_global_dtors_aux>:
6b0: 80 3d 59 09 20 00 00 cmpb $0x0,0x200959(%rip) # 201010 <__TMC_END__>
6b7: 75 2f jne 6e8 <__do_global_dtors_aux+0x38>
6b9: 48 83 3d 37 09 20 00 cmpq $0x0,0x200937(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
6c0: 00
6c1: 55 push %rbp
6c2: 48 89 e5 mov %rsp,%rbp
6c5: 74 0c je 6d3 <__do_global_dtors_aux+0x23>
6c7: 48 8b 3d 3a 09 20 00 mov 0x20093a(%rip),%rdi # 201008 <__dso_handle>
6ce: e8 0d ff ff ff callq 5e0 <__cxa_finalize@plt>
6d3: e8 48 ff ff ff callq 620 <deregister_tm_clones>
6d8: c6 05 31 09 20 00 01 movb $0x1,0x200931(%rip) # 201010 <__TMC_END__>
6df: 5d pop %rbp
6e0: c3 retq
6e1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
6e8: f3 c3 repz retq
6ea: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
00000000000006f0 <frame_dummy>:
6f0: 55 push %rbp
6f1: 48 89 e5 mov %rsp,%rbp
6f4: 5d pop %rbp
6f5: e9 66 ff ff ff jmpq 660 <register_tm_clones>
00000000000006fa <recurse>:
6fa: 55 push %rbp
6fb: 48 89 e5 mov %rsp,%rbp
6fe: 48 81 ec 20 00 02 00 sub $0x20020,%rsp
705: 89 bd ec ff fd ff mov %edi,-0x20014(%rbp)
70b: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
712: 00 00
714: 48 89 45 f8 mov %rax,-0x8(%rbp)
718: 31 c0 xor %eax,%eax
71a: 48 8d 95 f0 ff fd ff lea -0x20010(%rbp),%rdx
721: 8b 85 ec ff fd ff mov -0x20014(%rbp),%eax
727: 89 c6 mov %eax,%esi
729: 48 8d 3d 64 01 00 00 lea 0x164(%rip),%rdi # 894 <_IO_stdin_used+0x4>
730: b8 00 00 00 00 mov $0x0,%eax
735: e8 86 fe ff ff callq 5c0 <printf@plt>
73a: c7 85 f0 ff fd ff ff movl $0x3fff,-0x20010(%rbp)
741: 3f 00 00
744: 8b 85 f0 ff fd ff mov -0x20010(%rbp),%eax
74a: 8b 95 ec ff fd ff mov -0x20014(%rbp),%edx
750: 83 ea 01 sub $0x1,%edx
753: 48 98 cltq
755: 89 94 85 f0 ff fd ff mov %edx,-0x20010(%rbp,%rax,4)
75c: 8b 85 f0 ff fd ff mov -0x20010(%rbp),%eax
762: 48 98 cltq
764: 8b 84 85 f0 ff fd ff mov -0x20010(%rbp,%rax,4),%eax
76b: 85 c0 test %eax,%eax
76d: 75 07 jne 776 <recurse+0x7c>
76f: b8 ff ff ff ff mov $0xffffffff,%eax
774: eb 19 jmp 78f <recurse+0x95>
776: 8b 85 f0 ff fd ff mov -0x20010(%rbp),%eax
77c: 48 98 cltq
77e: 8b 84 85 f0 ff fd ff mov -0x20010(%rbp,%rax,4),%eax
785: 89 c7 mov %eax,%edi
787: e8 6e ff ff ff callq 6fa <recurse>
78c: 83 e8 01 sub $0x1,%eax
78f: 48 8b 4d f8 mov -0x8(%rbp),%rcx
793: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx
79a: 00 00
79c: 74 05 je 7a3 <recurse+0xa9>
79e: e8 0d fe ff ff callq 5b0 <__stack_chk_fail@plt>
7a3: c9 leaveq
7a4: c3 retq
00000000000007a5 <main>:
7a5: 55 push %rbp
7a6: 48 89 e5 mov %rsp,%rbp
7a9: 48 83 ec 20 sub $0x20,%rsp
7ad: 89 7d ec mov %edi,-0x14(%rbp)
7b0: 48 89 75 e0 mov %rsi,-0x20(%rbp)
7b4: c7 45 f8 64 00 00 00 movl $0x64,-0x8(%rbp)
7bb: 83 7d ec 01 cmpl $0x1,-0x14(%rbp)
7bf: 7e 16 jle 7d7 <main+0x32>
7c1: 48 8b 45 e0 mov -0x20(%rbp),%rax
7c5: 48 83 c0 08 add $0x8,%rax
7c9: 48 8b 00 mov (%rax),%rax
7cc: 48 89 c7 mov %rax,%rdi
7cf: e8 fc fd ff ff callq 5d0 <atoi@plt>
7d4: 89 45 f8 mov %eax,-0x8(%rbp)
7d7: 8b 45 f8 mov -0x8(%rbp),%eax
7da: 89 c7 mov %eax,%edi
7dc: e8 19 ff ff ff callq 6fa <recurse>
7e1: 89 45 fc mov %eax,-0x4(%rbp)
7e4: 8b 55 fc mov -0x4(%rbp),%edx
7e7: 8b 45 f8 mov -0x8(%rbp),%eax
7ea: 89 c6 mov %eax,%esi
7ec: 48 8d 3d b3 00 00 00 lea 0xb3(%rip),%rdi # 8a6 <_IO_stdin_used+0x16>
7f3: b8 00 00 00 00 mov $0x0,%eax
7f8: e8 c3 fd ff ff callq 5c0 <printf@plt>
7fd: b8 00 00 00 00 mov $0x0,%eax
802: c9 leaveq
803: c3 retq
804: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
80b: 00 00 00
80e: 66 90 xchg %ax,%ax
0000000000000810 <__libc_csu_init>:
810: 41 57 push %r15
812: 41 56 push %r14
814: 49 89 d7 mov %rdx,%r15
817: 41 55 push %r13
819: 41 54 push %r12
81b: 4c 8d 25 86 05 20 00 lea 0x200586(%rip),%r12 # 200da8 <__frame_dummy_init_array_entry>
822: 55 push %rbp
823: 48 8d 2d 86 05 20 00 lea 0x200586(%rip),%rbp # 200db0 <__init_array_end>
82a: 53 push %rbx
82b: 41 89 fd mov %edi,%r13d
82e: 49 89 f6 mov %rsi,%r14
831: 4c 29 e5 sub %r12,%rbp
834: 48 83 ec 08 sub $0x8,%rsp
838: 48 c1 fd 03 sar $0x3,%rbp
83c: e8 3f fd ff ff callq 580 <_init>
841: 48 85 ed test %rbp,%rbp
844: 74 20 je 866 <__libc_csu_init+0x56>
846: 31 db xor %ebx,%ebx
848: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
84f: 00
850: 4c 89 fa mov %r15,%rdx
853: 4c 89 f6 mov %r14,%rsi
856: 44 89 ef mov %r13d,%edi
859: 41 ff 14 dc callq *(%r12,%rbx,8)
85d: 48 83 c3 01 add $0x1,%rbx
861: 48 39 dd cmp %rbx,%rbp
864: 75 ea jne 850 <__libc_csu_init+0x40>
866: 48 83 c4 08 add $0x8,%rsp
86a: 5b pop %rbx
86b: 5d pop %rbp
86c: 41 5c pop %r12
86e: 41 5d pop %r13
870: 41 5e pop %r14
872: 41 5f pop %r15
874: c3 retq
875: 90 nop
876: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
87d: 00 00 00
0000000000000880 <__libc_csu_fini>:
880: f3 c3 repz retq
Disassembly of section .fini:
0000000000000884 <_fini>:
884: 48 83 ec 08 sub $0x8,%rsp
888: 48 83 c4 08 add $0x8,%rsp
88c: c3 retq
*/
我们可以看到,递归不仅仅使存储空间迅速减少,还会使代码在编译时变得更加复杂。所以一定要慎用递归!