1

再帰を使用して文字列を逆にするためのコードを書いています。私の方法は正しいと信じていますが、セグメンテーション エラーが発生し続けており、それがどこから来ているのかわかりません。私のすべての調査によると、それは私が「記憶に関して奇妙なことをしている」ことを意味します。私はこれに十分慣れていないため、これらの種類のエラーはまだ困惑しているため、ここでの助けをいただければ幸いです。これが私のコードです:

#include <iostream>
#include <string>
using namespace std;
class Palindrome
{
    int front;
    int back;
public:
    Palindrome();
    string reverse(string word)
    {
        int len = word.length()-1;
        if (back == 0) {
            back = len;
        }
        if (front >= back)
            return word;
        else{
            char first = word[front];
            char last = word[back];
            word[front] = last;
            word[back] = first;
            front += 1;
            back -= 1;
            reverse(word);
        }
    }
};

Palindrome::Palindrome(){
front = 0;
back = 0;
}
4

3 に答える 3

1

私はあなたのコードを試してみましたが、1 回の呼び出しでも「アクセス違反」が発生しました。他の回答やコメントで説明されている初期化の問題に加えて、セグフォルトの原因は、「リバース」への再帰呼び出しの前に「リターン」がないことです。あなたは書く必要がありますreturn reverse(word);

Visual Studio では、元のコードは次のようになります: warning C4715: 'Palindrome::reverse' : すべてのコントロール パスが値を返すわけではありません。

詳細については、この質問を参照してください。

両方の修正を加えたバージョンの reverse() を次に示します。

    string reverse(string word)
    {
        int len = word.length()-1;
        if (back == 0) 
        {
            back = len;
        }
        if (front >= back)
        {
            front = 0;
            back = 0;
            return word;
        }
        else
        {
            char first = word.at(front);
            char last = word.at(back);
            word.at(front) = last;
            word.at(back) = first;
            front += 1;
            back -= 1;
            return reverse(word);
        }
    }
于 2012-04-25T17:20:35.103 に答える
1

ジェイコブ・エイブラハムが言おうとしてfrontいたことは反復されますが、ゼロにリセットされることはありません。そのため、2 回目に呼び出すと、2 番目の単語が長いか短いかに応じて、セグメンテーション違反が発生するか、誤った結果が生成されます。

さらに、マーク B がすでに示唆しているのは、関数algorithm全体を含めて置き換えることができるということです。Palindrome::reverse

std::reverse(word.begin(), word.end());

何よりも、デバッガーの使用方法を習得したり、将来的には、少なくともこれらの種類の質問に対して特定のエラー メッセージを表示したりすることが役に立ちます。

編集: 再帰 (たとえば、それ自体を呼び出す関数) を追加するのを忘れたのは、実行スタックが非常に小さいため、通常は悪い考えです。この場合、前述の問題を修正した後でも、特に長い文字列に対してスタック オーバーフローが発生します。 . 実際には、この特定のコードがわかりにくくなります。

于 2012-04-25T17:04:56.827 に答える
1

個人的には、再帰とオブジェクトの混合はやや奇妙だと思います。オブジェクトの基本的な概念の 1 つは、追跡したい状態をオブジェクトが保持するということです。再帰の基本概念の 1 つは、追跡したい状態を実行スタックが保持することです。

この場合、追跡したい状態は、処理された文字列の量/処理されていない文字列の量です。オブジェクトがなくても追跡できます。

これは宿題の質問のようなにおいがします。しかし、答えを手渡すだけでは、ヒントを与えることはできません。私ができる最善のことは、私の答えを作ることです(1)文字列を含むがこれに限定されないコンテナを逆にします。(2) STL のようなインターフェイス (つまり、反復子) を使用します。(3) 文字列のコピーを逆にするのではなく、その場で文字列を逆にします。

#include <algorithm> // std::swap

// the other headers are only for my example on how to use the code
#include <iostream>
#include <iterator>
#include <string>
#include <list>

template<typename Itor> void reverse_with_recursion(Itor begin, Itor end)
{
    using std::swap; // same trick used by the STL to get user-defined swap's,
                     // but fall back to std::swap if nothing else exists:
                     // http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Interfaces

    // if begin and end are pointing at the same element,
    // then we have an empty string and we're done
    if (begin == end) {
        return;
    }

    // the STL follows the pattern that end is one element after
    // the last element;  right now we want the last element
    --end;

    // if begin and end are pointing at the same element *now*,
    // then we have a single character string and we're done
    if (begin == end) {
        return;
    }

    swap(*begin, *end);
    return reverse_with_recursion(++begin, end);
}

int main()
{
    std::string foo("hello world");
    reverse_with_recursion(foo.begin(), foo.end());

    std::cout << foo << '\n';

    std::list<int> bar;
    for (int i = 0; i < 10; ++i) {
       bar.push_back(i);
    }

    reverse_with_recursion(bar.begin(), bar.end());

    std::copy(bar.begin(),
              bar.end(),
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
于 2012-04-25T17:40:47.253 に答える