Наивная обратная итерация строки, бесконечный цикл и/или ошибка утверждения C++/Visual Studio 2022

Я пытаюсь выполнить обратную итерацию по строке, но получаю ошибку утверждения для оператора [] в последней версии VS.

int foo() {
    std::string s = "s";
    for (int i = (s.size() - 1); i >= 0; i--) {
        std::cout << s[i] << std::endl;
    }
    return 0;
}

Комментирование строки cout дает предупреждение о бесконечном цикле и действительно входит в бесконечный цикл:

int foo2() {
    std::string s = "s";
    for (int i = (s.size() - 1); i >= 0; i--) {
        //std::cout << s[i] << std::endl;
    }
    return 0;
}

Итерации вперед прекрасно работают как с циклом for, так и без него:

int bar() {
    std::string s = "s";
    for (int i = 0; i < s.size(); i++) {
        std::cout << s[i] << std::endl;
    }
    return 0;
}

Я использую стандарт С++ 14 и тот же код, который использовался для работы с другими компиляторами/старыми версиями VS. Та же проблема существует для любого размера строки (хотя это не имеет значения). Я понимаю, что могу изменить код для использования и разыменования указателя вместо использования int, но хочу понять, почему это больше не работает, почему это небезопасно или неправильно, или что еще мне не хватает. Спасибо!

Я не понимаю, как этот цикл for (int i = (s.size() - 1); i >= 0; i--) { может быть бесконечным циклом for, когда s.size() не равен 0 .

Vlad from Moscow 05.05.2022 15:26
но хочу понять, почему это больше не работает -- Вероятно, это никогда не работало, и вам просто (не)повезло, что это сработало. Вот к чему может привести неопределенное поведение.
PaulMcKenzie 05.05.2022 15:27

@PaulMcKenzie Где вы нашли здесь неопределенное поведение?!

Vlad from Moscow 05.05.2022 15:35

@VladfromMoscow -- Просто читаю заявление комментаторов -- Я использую стандарт С++ 14, и тот же код используется для работы с другими компиляторами/старыми версиями VS.

PaulMcKenzie 05.05.2022 15:41

Вы уверены, что именно в этом ваша проблема? На самом деле в вашем коде нет никаких проблем, кроме преобразования std::size_t в int, и ваши примеры кода правильно работают в gcc/clang/msvc для меня.

Kaldrr 05.05.2022 16:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
73
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Commenting out the cout line gives infinite loop warning and indeed enters infinite loop

s.size() — это unsigned long, поэтому оно никогда не может быть меньше 0. Поведение сужающего преобразования из-за присвоения unsigned long к int является поведением, определяемым реализацией, хотя это не должно быть проблемой при присвоении unsigned long значения 0 к int, по крайней мере, я так не думаю, но кто знает, у MSVC есть свои причуды.

Похоже, компилятор достаточно умен, чтобы предсказать, что не может быть бесконечного цикла, когда присутствует это конкретное тело цикла for, что наводит меня на мысль, что при доступе к массиву может быть выдано исключение, возможно, за пределами доступа.

Использование отладчика для отслеживания значения i — лучший способ понять, что происходит.

Где там в цикле использовалось отрицательное значение i?!

Vlad from Moscow 05.05.2022 15:46

Дело в том, что я вообще не должен превышать 0 по критерию цикла "i >= 0;"

Z_C 05.05.2022 15:55

@Z_C, сделай одолжение, в коде после строки std::string s = "s"; добавь строку std::cout << s.size() - 2; и посмотри, что получится.

anastaciu 05.05.2022 15:59

@anastaciu входит в бесконечный цикл, но на этот раз печатает экран, полный чисел. Также, если я что-то не упустил, он не должен выходить за пределы нуля не потому, что s.size() не может быть отрицательным, а потому, что буквально это было критерием цикла for .

Z_C 05.05.2022 16:04

нет, 18446744073709551615

Z_C 05.05.2022 16:06

О, хорошо, это максимальное значение 8-байтового беззнакового длинного - 1, принцип тот же, теперь вы видите, что i никогда не будет отрицательным, не так ли?

anastaciu 05.05.2022 16:08

Z_C извините, я думал, что вы используете auto i, поэтому, если i это int, мой ответ не применим.

anastaciu 05.05.2022 16:10

теперь я попробовал с auto, затем с int, и auto дает бесконечный цикл и / или ошибку утверждения, а с int он отлично себя ведет. не знаю, что происходит... число все еще 18446744073709551615, хотя

Z_C 05.05.2022 16:14

@Z_C, что и следовало ожидать, вы все равно печатаете значение, назначение int сужается от unsigned long, его реализация определяет, как это должно работать, но присвоение unsigned long значения 0int не должно быть проблемой, поэтому неясно почему ваш код делает то, что он делает.

anastaciu 05.05.2022 16:17

@Z_C используйте отладчик, чтобы отследить значение i и посмотреть, к чему оно вас приведет.

anastaciu 05.05.2022 16:25

@Z_C, то есть поместите точку останова в строку цикла for, запустите его и используйте F10, чтобы перейти, вы можете отслеживать значение i в разделителе Locals ниже.

anastaciu 05.05.2022 16:30

@VladfromMoscow Я неправильно прочитал, и у меня сложилось впечатление, что ОП использует auto i, так что вы правы, мой диагноз был неверным.

anastaciu 05.05.2022 16:32

@anastaciu Я думаю, что вы были правы с самого начала, я думаю, что оригинал мог быть ошибкой обновления, когда я изменил типы переменных, и именно auto выдает ошибки, с int все в порядке. С int i инициализируется правильно, затем уменьшается до 0, после чего цикл останавливается. С auto он уменьшается до нуля, затем меняется на 18446744073709551615 и так далее. Так что, похоже, действительно автоматически назначается unsigned int. Единственный вопрос: почему цикл не останавливается, когда i становится 0?

Z_C 05.05.2022 17:09

@Z_C а, я так и знал :D В любом случае, кроме шуток, я рад, что ты решил свою проблему.

anastaciu 05.05.2022 17:15
Ответ принят как подходящий

Может ли быть так, что ваш int по умолчанию является беззнаковым и, следовательно, уменьшается, когда i = 0 приводит к высокому значению?

Как упоминал @PeteBecker, int должен быть подписан и не должен переполняться. Однако я предполагаю, что ваш фактический код не использует int.

я не должен уменьшаться после 0

Z_C 05.05.2022 15:56

int всегда подписан. Просто char может быть как подписанным, так и не подписанным.

Pete Becker 05.05.2022 16:02

@PeteBecker: согласен, но я предполагаю, что его минимальный пример - это не тот, который зацикливается бесконечно, и что в его реальном коде он использует size_t i.

gchapuis 06.05.2022 09:42

@gchapuis - вы можете отредактировать свой ответ, чтобы сказать это.

Pete Becker 06.05.2022 15:14

@PeteBecker Вот, пожалуйста :)

gchapuis 06.05.2022 15:17

На самом деле проблема изначально была вызвана «auto» i, а не «int» i, но не обновлена ​​​​должным образом. Auto дал i тип unsigned int, который переполнил оператор [] или вызвал переключение бесконечного цикла с нуля на 18446744073709551615 после завершения цикла i = 0 и выполнения i--.

Спасибо всем за полезные ответы!

Другие вопросы по теме