为什么我不能递归调用我的函数?

Why can't I call my function recursively?

本文关键字:我的 函数 调用 递归 不能 为什么      更新时间:2023-10-16

我试图编译这段代码,以便反转字符串:

void reverse(char *str, int n)
{
    if (n==0 || n==1) {
        return;         //acts as quit
    } else {
        char i = str[0];    //1st position of string
        char j = str[n-1];  //Last position of string
        char temp = str[i];
        str[i] = str[j];    //Swap
        str[j] = temp;
        reverse(str[i+1],n-1);  // <-- this line
    }
}
#include <iostream>
int main()
{
    char *word = "hello";
    int n = sizeof word;
    reverse(word, n);
    std::cout << word << std::endl;
    return 0;
}

编译器报告一个错误,我递归地调用reverse():

reverse(str[i+1], n-1)下由char转化为char*无效。

为什么?

str[i+1]是一个字符,而不是指向字符的指针;因此出现了错误信息。

当你输入函数时,str指向你要与n交换的字符:离str的第一个字符。
在递归中需要做的是增加指针的值,使其指向下一个字符。
您还需要将n减少两个,因为它应该与str + 1保持距离,而不是与str
(这很容易出错;请查看此答案的编辑历史以获取示例。

在交换时,您还使用字符串中的字符作为字符串的索引。
(如果输入的是"ab",则输入的是char temp = str['a']; str['a'] = str['b']; str['b'] = temp;。这显然是不正确的。
str[0]不是第一个字符的位置,它第一个字符。

如果允许,请使用std::swap,否则请参见下文。

更多问题:你不应该使用sizeof word,因为这是4或8取决于你的目标架构-它相当于sizeof(char*)
您应该使用strlen来查找字符串的长度。

进一步,您应该得到

的警告
char *word = "hello";

,因为这种特殊的转换是危险的——"hello"const数组,修改它是未定义的。
(如果你从来没有修改过数组,这是安全的,但是你修改了,所以它不是。)
将其复制到非const数组中:

char word[] = "hello";

并增加编译器的警告级别。

这里有一个固定的版本:

void reverse(char *str, int n)
{   
    if(n <= 1) // Play it safe even with negative n    
    {
        return;
    }
    else
    {
        // You could replace this with std::swap(str[0], str[n-1])
        char temp = str[0];    //1st character in the string
        str[0] = str[n-1];    //Swap
        str[n-1] = temp;
        // n - 2 is one step closer to str + 1 than n is to str.
        reverse(str + 1, n - 2);
    }
}
int main()
{
    char word[] = "hello";
    // sizeof would actually work here, but it's fragile so I prefer strlen.
    reverse(word, strlen(word));
    std::cout << word << std::endl;
}

我要剖析你的代码,就像你在code Review上发表的那样。毕竟,你确实要求进行其他观察……

首先,

char *word = "hello";

编译器应该警告您,将char*指向文字字符串是未定义的行为(如果不是,请确保您实际上启用了一组良好的警告)。由于历史原因,许多编译器在默认情况下很少发出警告)。你需要确保你有一个可写的字符串;你可以使用char[]:

char word[] = "hello";

下一行

int n = sizeof word;

现在已经改变了意思,但仍然是错误的。在您的原始代码中,它是指向char指针的大小,这不太可能与单词"hello"的长度相同。更改为char[]后,它现在是6个字符的数组的大小,即6。第六个字符是结束字符串字面值的NUL。您可能希望使用strlen()函数而不是sizeof操作符。

转到reverse():

从字符串中的位置读取字符,然后使用这些字符对其进行索引。这不是您想要的,并且GCC警告不要使用普通char作为索引,因为它可能是有符号的或无符号的。你只想在一个地方索引,你的ij是不必要的。

最后,你问的问题。str[i+1]是位置i+1字符,但是您的函数需要一个指向字符的指针,即str+i+1。或者,因为我们算出我们不需要i,只需要str+1

还请注意,您需要从n中减去2,而不是1,因为它将用作str+1中的字符计数。如果你只减去1,你将总是与最后一个字符交换,并且你将获得"滚动"而不是"反转"。


这是一个工作版本:

void reverse(char *str, int n)
{
    if (n < 2)
        // end of recursion
        return;         //acts as quit
    char temp = str[0];
    str[0] = str[n-1];    //Swap
    str[n-1] = temp;
    reverse(str+1,n-2);
}
#include <iostream>
#include <cstring>
int main()
{
    char word[] = "hello";
    int n = std::strlen(word);
    reverse(word, n);
    std::cout << word << std::endl;
}

我们可以做进一步的修改。例如,我们可以使用std::swap来更清楚地表达切换。我们可以传递一对指针而不是指针和长度:

#include <utility>              // assuming C++11 - else <algorithm>
void reverse(char *str, char *end)
{
    if (end <= str)
        // end of recursion
        return;
    std::swap(*str, *end);
    reverse(str+1, end-1);
}

并使用reverse(word, word+n-1)调用。


最后(因为我不打算提到std::reverse()),这里是惯用的迭代版本:

void reverse(char *str, char *end)
{
    while (str < end)
        std::swap(*str++, *end--);
}

使用如下:

     reverse(&str[i+1],n-1);

传递(i+1)位非值的地址。