Ошибка сегментации в std::меньше

#c #segmentation-fault

#c #ошибка сегментации

Вопрос:

У меня есть следующий код (C 0x):

 const set<char> s_special_characters =  { '(', ')', '{', '}', ':' };

void nectar_loader::tokenize( string amp;line, const set<char> amp;special_characters )
{
    auto it = line.begin();
    const auto not_found = special_characters.end();

    // first character special case
    if( it != line.end() amp;amp; special_characters.find( *it ) != not_found )
        it = line.insert( it 1, ' ' )   1;

    while( it != line.end() )
    {
        // check if we're dealing with a special character
        if( special_characters.find(*it) != not_found ) // <----------
        {
            // ensure a space before
            if( *(it-1) != ' ' )
                it = line.insert( it, ' ' )   1;
            // ensure a space after
            if( (it 1) != line.end() amp;amp; *(it 1) != ' ' )
                it = line.insert( it 1, ' ');
            else
                line.append(" ");
        }
          it;
    }
}
  

с ошибкой, указывающей на указанную строку. Это приводит к ошибке сегментации с этим обратным отслеживанием gdb:

 #0  0x000000000040f043 in std::less<char>::operator() (this=0x622a40, __x=@0x623610, __y=@0x644000)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c  /4.5.2/bits/stl_function.h:230
#1  0x000000000040efa6 in std::_Rb_tree<char, char, std::_Identity<char>, std::less<char>, std::allocator<char> >::_M_lower_bound (this=0x622a40, __x=0x6235f0, __y=0x622a48, __k=@0x644000)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c  /4.5.2/bits/stl_tree.h:1020
#2  0x000000000040e840 in std::_Rb_tree<char, char, std::_Identity<char>, std::less<char>, std::allocator<char> >::find (this=0x622a40, __k=@0x644000)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c  /4.5.2/bits/stl_tree.h:1532
#3  0x000000000040e4fd in std::set<char, std::less<char>, std::allocator<char> >::find (this=0x622a40, __x=@0x644000)
    at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c  /4.5.2/bits/stl_set.h:589
#4  0x000000000040de51 in ambrosia::nectar_loader::tokenize (this=0x7fffffffe3b0, line=..., special_characters=...)
    at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:146
#5  0x000000000040dbf5 in ambrosia::nectar_loader::fetch_line (this=0x7fffffffe3b0)
    at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:112
#6  0x000000000040dd11 in ambrosia::nectar_loader::fetch_token (this=0x7fffffffe3b0, token=...)
    at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:121
#7  0x000000000040d9c4 in ambrosia::nectar_loader::next_token (this=0x7fffffffe3b0)
    at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:72
#8  0x000000000040e472 in ambrosia::nectar_loader::extract_nectar<std::back_insert_iterator<std::vector<ambrosia::target> > > (this=0x7fffffffe3b0, it=...)
    at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:43
#9  0x000000000040d46d in ambrosia::drink_nectar<std::back_insert_iterator<std::vector<ambrosia::target> > > (filename=..., it=...)
    at ../../ambrosia/Library/Source/Ambrosia/nectar.cpp:75
#10 0x00000000004072ae in ambrosia::reader::event (this=0x623770)
  

Я в растерянности и понятия не имею, где я делаю что-то не так. Любая помощь очень ценится.

РЕДАКТИРОВАТЬ: строка в момент сбоя равна

подзаголовок Ambrosia : библиотека libAmbrosia

Обновить:

Я заменил вышеупомянутую функцию, следуя предложениям в комментариях / ответах. Ниже приведен результат.

 const string tokenize( const string amp;line, const set<char> amp;special_characters )
{
    const auto not_found = special_characters.end();
    const auto end = line.end();
    string resu<

    if( !line.empty() )
    {
        // copy first character
        result  = line[0];

        char previous = line[0];
        for( auto it = line.begin() 1; it != end;   it )
        {
            const char current = *it;

            if( special_characters.find(previous) != not_found )
                result  = ' ';

            result  = current;
            previous = current;
        }
    }
    return resu<
}
  

Комментарии:

1. Что это за строка, переданная nectar_loader::tokenize в случае ошибки сегментации? (Это не видно на обратном пути выше.)

2. добавлено строковое содержимое к вопросу.

Ответ №1:

Другое предположение заключается в том, что line.append(" ") иногда это приводит к недействительности it , в зависимости от исходной емкости строки.

Комментарии:

1. insert также может привести к аннулированию итераторов.

2. @Gareth: вот почему я использую return итератор редактирования при вызовах insert. Я не смог выполнить вызов append.

3. @rubenvb: о да, так и есть; извините.

4. Я принял этот ответ, потому что это наиболее вероятный виновник, хотя в конкретном случае, когда происходит сбой, это не кажется очень вероятным.

Ответ №2:

Вы не проверяете это it != line.end() перед первым разыменованием it .

Комментарии:

1. Верно, но трудно понять, как это могло привести к segfault в точке, в которой, по утверждению rubenvb, находится ошибка. (Конечно, мы не знаем абсолютно точно, что это реальное местоположение ошибки.)

2. Может быть, но добавление этой проверки к первой if: if( it != line.end() amp;amp; special_characters... ) не устраняет сбой :( . Я также попробовал первый if внутри цикла. Никаких изменений ни в том, ни в другом случае.

3. Отредактировал первое if. Следующее if (первое в цикле) не нуждается в проверке, как это уже было сделано в условии while, но следующему if требуется способ проверки на наличие недопустимого итератора. Могу ли я проверить it >= line.begin() или что-то в этом роде?

4. Причина, по которой он выполняет сегментацию внутри find вместо *i того, find что он принимает свой аргумент по ссылке. Код просто передает адрес, который указывает на недопустимую память, но он не завершится сбоем, пока не попытается прочитать с этого адреса.

Ответ №3:

Я не смог обнаружить ошибку, я бы предложил выполнять итерацию медленно с помощью отладчика, поскольку вы определили проблему.

Я просто отмечу, что в целом изменение того, что вы повторяете, чрезвычайно подвержено сбою.

Я бы рекомендовал использовать Boost Tokenizer, а точнее: boost::token_iterator в сочетании с boost::char_separator (пример кода включен).

Затем вы могли бы просто создать новую string из первой и вернуть новую строку из функции. Ускорение вычислений должно покрывать выделение памяти.

Комментарии:

1. Я подумал о создании подкласса istream or ( stream_buf того, который должен быть подклассом) и работе с гораздо более низкого уровня (подкласса operator>> ). Разве это не было бы лучшим решением STL в данном случае?

2. @rubenvb: Я недостаточно знаю об особенностях istream . Кажется, сложнее добавить необязательное поведение (например, полное обрезание на обоих концах).