【问题标题】:binary search (Recursive implementation) [duplicate]二分搜索(递归实现)
【发布时间】:2018-12-08 17:56:10
【问题描述】:

我绑在递归中编写二进制搜索代码。我首先写的是:

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;
}

因为在第一个代码中我们可能没有返回值。
我的问题是为什么第一个代码有效?

【问题讨论】:

  • 未定义的行为是未定义的。
  • @melpomene 愚蠢的问题,但这实际上是 C 中未定义的行为吗?还是只是未指定?我很确定它是未定义的,但我很难找到关于它的参考。
  • @templatetypedef "如果到达终止函数的},并且调用者使用了函数调用的值,则行为未定义。" @ 987654321@

标签: c function recursion binary-search


【解决方案1】:

欢迎来到未定义行为的精彩世界。在 C 语言中,如果您忘记从函数返回一个值并尝试使用该值,您会得到所谓的未定义行为,这意味着对随后可能发生的事情没有任何要求。完全有可能纯巧合的代码将在您的系统上运行,但是在另一个系统上编译相同的代码可能最终导致相同的代码无法产生正确的值。虽然我无法想象这种情况会发生,但从技术上讲,未定义的行为可能会导致您的程序在播放“Gangnam Style”的歌词时重新格式化用户的硬盘。

这可能在您的系统上起作用的原因是,在许多体系结构中,小的返回值存储在专门的寄存器中,而返回语句的编译方式是将值加载到该寄存器中,然后跳出功能。因此,如果您最终在递归链中返回一个值,然后忘记在其他地方返回值,则该原始值可能巧合地仍在寄存器中,就好像您已正确返回它一样。但是你不能依赖它,如上所述 - 它不是便携式的。

【讨论】:

    【解决方案2】:

    扩展@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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-18
      相关资源
      最近更新 更多