Выражение: Итератор строки не может быть разыменован

Мне трудно использовать std::string::iterators в С++. Этот код отлично компилируется (по-прежнему не дает правильного вывода, но это моя вина: TODO, исправление алгоритма) в Dev-C++, и я не получаю ошибок времени выполнения. Ошибка связана с Visual Studio Express 2008 C++, где я получаю сообщение об ошибке, указывающее на ‹ xstring>: «Выражение: итератор строки не может быть разыменован» и указывает на строку 112 файла ‹ xstring>.

Моя отладка говорит мне, что я, возможно, пытаюсь разыменовать конец ввода предложения, но я не вижу, где. Кто-нибудь может пролить свет?

std::string wordWrap(std::string sentence, int width)
{    
    std::string::iterator it = sentence.begin();

    //remember how long next word is
    int nextWordLength = 0;
    int distanceFromWidth = width;

    while (it < sentence.end())
    {
       while (*it != ' ' && it != sentence.end())
       {
          nextWordLength++;
          distanceFromWidth--;
          it++;
       }

       if (nextWordLength > distanceFromWidth)
       {
          *it = '\n';
          distanceFromWidth = width;
          nextWordLength = 0;
       }

       //skip the space
       it++;

   }

   return sentence;    
}

person jkeys    schedule 30.06.2009    source источник


Ответы (3)


Во-первых, используйте оператор!=() на итераторах, а не оператор‹():

while (it != sentence.end())

Во-вторых, это наоборот: while (*it != ' ' && it != sentence.end())

Вы что-то делаете с итератором, чем проверяете, действителен ли итератор. Скорее, вы должны сначала проверить, действительно ли это:

while (it != sentence.end() && *it != ' ')

В-третьих, вы должны использовать ++iterator вместо iterator++, хотя это не связано с вашим сбоем.


В-четвертых, главная проблема здесь:

*it = '\n';

Из-за предшествующей проверки while (it != sentence.end() можно достичь разыменования этого итератора, находясь в конце. Исправление будет заключаться в следующем:

if (it != sentence.end() && nextWordLength > distanceFromWidth)

Так что теперь, если вы дошли до конца, вы останавливаетесь.


После исправления предыдущей проблемы теперь единственная проблема заключается в следующем:

//skip the space
++it;

Это предполагает, что символ, который вы пропускаете, на самом деле является пробелом. Но как насчет конца строки? Запустите эту функцию с этой строкой:

"a test string " // <- space at end

И это удастся; он пропускает пробел, помещая итератор в end(), цикл завершается и завершается успехом.

Однако без пробела он вылетит, потому что вы дошли до конца и пропускаете мимо конца. Чтобы исправить, добавьте проверку:

//skip the space
if (it != sentence.end())
{
    ++it;
}

В результате получается этот окончательный код:

std::string wordWrap(std::string sentence, int width)
{    
    std::string::iterator it = sentence.begin();

    //remember how long next word is
    int nextWordLength = 0;
    int distanceFromWidth = width;

    while (it != sentence.end())
    {
        while (it != sentence.end() && *it != ' ')
        {
            nextWordLength++;
            distanceFromWidth--;
            ++it;
        }

        if (it != sentence.end() && nextWordLength > distanceFromWidth)
        {
            *it = '\n';
            distanceFromWidth = width;
            nextWordLength = 0;
        }

        //skip the space
        if (it != sentence.end())
        {
            ++it;
        }

    }

    return sentence;    
}

Вы можете заметить, что это выглядит так, как будто в нем много избыточных проверок. Это можно исправить:

std::string wordWrap(std::string sentence, int width)
{    
    std::string::iterator it = sentence.begin();

    //remember how long next word is
    int nextWordLength = 0;
    int distanceFromWidth = width;

    while (it != sentence.end())
    {
        while (*it != ' ')
        {
            nextWordLength++;
            distanceFromWidth--;

            ++it;

            // check if done
            if (it == sentence.end())
            {
                return sentence;
            }
        }

        if (nextWordLength > distanceFromWidth)
        {
            *it = '\n';
            distanceFromWidth = width;
            nextWordLength = 0;
        }

        //skip the space
        ++it;
    }

    return sentence;    
}

Надеюсь, это поможет!

person GManNickG    schedule 30.06.2009
comment
+1. Я исправил код в пункте 2, чтобы он совпадал с полным блоком кода и объяснением. - person David Rodríguez - dribeas; 30.06.2009
comment
Спасибо. Я знаю о ++it (поэтому компилятор знает, что нужно скопировать лишний итератор) и об использовании !=. У меня возникали ошибки при компиляции с !=, и я дал себе передышку, так как знаю, что строки непрерывны. Я бы не стал делать то же самое с чем-то вроде стека. Вопрос: не лучше ли в вашем коде выкинуть исключение, если вы вышли из цикла? В вашем решении вы просто возвращаетесь. Похоже, это может скрыть некоторые серьезные проблемы. - person jkeys; 30.06.2009
comment
Я не уверен, что понимаю, что означает вне цикла. Приведенный выше код безопасен и не должен вызывать никаких исключений. - person GManNickG; 30.06.2009

while (*it != ' ' && it != sentence.end())

изменения к

while (it != sentence.end() && *it != ' ')

поэтому второе выражение не оценивается, если первое выражение ложно.

   if (nextWordLength > distanceFromWidth)

вероятно, следует изменить на

   if (it == sentence.end())
         break;
   if (nextWordLength > distanceFromWidth)
person Adrian Panasiuk    schedule 30.06.2009

Почти наверняка ваша ошибка является результатом:

*it = '\n';

Поскольку в предыдущем цикле while одним из ваших условий остановки является:

it != sentence.end()

Если это == предложение.конец(), то *это = '\n' не будет летать

Есть еще ошибки, но это та, которая вызывает вашу текущую проблему.

person csj    schedule 30.06.2009