Для цикла, работающего бесконечно с длиной строки в C ++ - PullRequest
0 голосов
/ 15 декабря 2018

Я очень удивлен таким поведением цикла for:

программа 1:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    for(int i=0;i<(s1.length()-s2.length()+1);i++)
    {
        cout<<"Hello\n";
    }
}

После ввода: s1 = "ab", s2 = "abcdef"

Это для цикла программы 1 выполняется бесконечно и печатает «Hello» бесконечное количество раз.

Принимая во внимание, что программа 2 (ниже) работает нормально для обоих входных данных строки s1 и s2.

программа 2:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s1,s2;
    cin>>s1>>s2;
    int len = (s1.length()-s2.length()+1);
    for(int i=0;i<len;i++)
    {
        cout<<"Hello\n";
    }
}

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

Ответы [ 2 ]

0 голосов
/ 15 декабря 2018

В вашем примере s1.length() оценивается как 2u (то есть 2, но в типе unsigned), s2.length() оценивается как 6u, s1.length() - s2.length() наиболее вероятно оценивается как 4294967292u (поскольку в неподписанных типах нет -4), а s1.length() - s2.length() + 1 оценивается как 4294967293u.

.length() и возвращает size_t в C ++, что является значением без знака.Вычитание значения без знака из другого значения без знака дает значение без знака, например, 1u - 2u может привести к 4294967295.

При смешивании значений со знаком и без знака (например, s.length() - 1 или i < s.length()), значение со знакомпреобразуется в без знака, например, -1 > 1u обычно составляет true, поскольку -1 преобразуется в 4294967295.Современные компиляторы предупредят вас о таком типе сравнений, если вы включите предупреждения.

Зная это, вы можете ожидать, что ваш цикл выполняется для 4 миллиардов итераций, но это не обязательно верно, поскольку i является подписаннымint, а если он 32-битный (скорее всего), он не может быть больше 2147483647.И в тот момент, когда ваша программа увеличивает его с 2147483647, происходит переполнение со знаком, что является неопределенным поведением в C ++.Так что ваш цикл может очень хорошо работать бесконечно.

Я подозреваю, что вы занимаетесь конкурентным программированием.Моя рекомендация для конкурентного программирования будет всегда приводить .length() к int всякий раз, когда вы хотите что-то вычислить.Вы можете создать макрос следующим образом:

#define sz(x) ((int)(x).size())

, а затем написать sz(s) вместо s.length() везде, чтобы избежать таких ошибок.

Однако, этот подход сильно недоволен в любой области программирования, где код должен жить дольше, чем несколько часов.Например, в отрасли или с открытым исходным кодом.В таких случаях используйте явные static_cast<int>(s.length()) / static_cast<ssize_t>(s.length()) каждый раз, когда вам это нужно.Или, что еще лучше, спросите об этом во время проверки кода, чтобы получить конкретные рекомендации относительно вашего кода, существует множество возможных предостережений, см. Комментарии ниже для некоторых примеров.

0 голосов
/ 15 декабря 2018

У меня еще не было возможности проверить это, поэтому я не могу сказать наверняка, но я сильно подозреваю, что это связано с тем, что string :: length () возвращает size_t , который является беззнаковым типом.Типы без знака переходят к максимальному значению, если они становятся отрицательными, поэтому 2-6 + 1 = -3, который становится 2 ^ 32-3, когда интерпретируется как 32-битный без знака.Это приводит к тому, что ваш цикл повторяется миллиарды раз, поэтому, по-видимому, не прерывается.В то время как во второй программе вы явно конвертируете в int со знаком, так что результат равен -3, как и ожидалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...