我不得不在递归中编写二进制搜索代码。我首先想知道这个:
int rSearch(int numbers[],int start, int end, int x){
int mid;
if(start <= end){
mid = (start + end)/2;
if(numbers[mid] == x)
return mid;
else if(numbers[mid] > x)
rSearch2(numbers, start, mid-1, x);
else
rSearch2(numbers, mid+1, end, x);
} else
return -1;
}
效果完美。但是在搜索之后,我明白我必须编写这样的代码:
int rSearch2(int numbers[],int start, int end, int x){
int mid;
if(start <= end){
mid = (start + end)/2;
if(numbers[mid] == x)
return mid;
else if(numbers[mid] > x)
return rSearch2(numbers, start, mid-1, x);
else
return rSearch2(numbers, mid+1, end, x);
} else
return -1;
}
因为在第一个代码中,我们可能没有返回值。
我的问题是为什么第一个代码行得通?
答案 0 :(得分:1)
欢迎来到未定义行为的美好世界。在C语言中,如果您忘记了从函数返回值并尝试使用该值,则会得到称为未定义行为的信息,这意味着对可能发生的情况没有任何要求。完全有可能,代码完全可以在您的系统上运行,但是在另一个系统上编译相同的代码可能最终导致相同的代码无法产生正确的值。尽管我无法想象会发生这种情况,但从技术上讲,未定义的行为可能会导致您的程序在播放“江南风格”的歌词时重新格式化用户的硬盘。
这可能在您的系统上起作用的原因是,在许多体系结构上,较小的返回值存储在专门的寄存器中,而返回语句的编译方式是将值加载到该寄存器中,然后跳出功能。因此,如果您最终在递归链中返回一个值,然后又忘记在其他地方返回值,则该原始值可能恰巧正确地返回了,而恰巧仍在寄存器中。但是如上所述,您不能依靠它-它不是可移植的。
答案 1 :(得分:1)
要在@templatetypedef上进行扩展(对不起,这将是不可读的注释):以Linux gcc x86_64为例:
$ gcc -S dummy.c -fverbose-asm -o -
...
# dummy.c:6: return mid;
movl -4(%rbp), %eax # mid, _12
jmp .L1 #
...
# dummy.c:8: rSearch2(numbers, start, mid-1, x);
...
call rSearch2 #
jmp .L7 #
.L5:
# dummy.c:10: rSearch2(numbers, mid+1, end, x);
...
call rSearch2 #
jmp .L7 #
.L2:
# dummy.c:13: return -1;
movl $-1, %eax #, _12
jmp .L1 #
.L7:
.L1:
# dummy.c:14: }
leave
.cfi_def_cfa 7, 8
ret
在此平台上,返回值存储在寄存器%eax
中。从汇编代码中可以明显看出
eax
已初始化或rSearch2()
时未触摸这是为什么编译器警告是程序员(最好的)朋友的经典案例:
$ gcc -Wall -Werror -S dummy.c -fverbose-asm -o dummy.s
dummy.c: In function ‘rSearch2’:
dummy.c:14:1: error: control reaches end of non-void function [-Werror=return-type]
}
^
cc1: all warnings being treated as errors