Цикл "for", який здається практично нескінченним


82

На даний момент я налагоджую деякий код і натрапив на цей рядок:

for (std::size_t j = M; j <= M; --j)

(Написав мій начальник, який у відпустці.)

Мені це здається по-справжньому дивним.

Що це робить? Для мене це схоже на нескінченну петлю.


31
size_tне підписаний, тому гарантовано обернеться до максимального значення при спробі припинити нуль, закінчуючи цикл. Все ще жахливий код.
BoBTFish

4
@Bathsheba Що робити, якщо Mце максимальне значення для size_t? Ти все ще вважаєш, що це розумно?
Торстен Діттмар,

8
@Barmar Багато не придумує, ніколи не придумує , тому існує ймовірність> 0. Що означає, що це рішення є дурним, коли є способи спростити його.
Торстен Діттмар,

38
Я не розумію голосування. Це питання зрозуміле - я не бачу, як його можна вдосконалити. Від нового користувача це питання насправді цікаве !
Вірсавія,

17
#define TRUE FALSEі поїхати у відпустку.
Лев

Відповіді:


68

std::size_tгарантується стандартом C ++ як unsignedтип. І якщо ви зменшуєте unsignedтип від 0, стандарт гарантує, що результат цього є найбільшим значенням для цього типу.

Це обернене значення завжди більше або дорівнює M1, тому цикл завершується.

Отже, j <= Mколи застосовується до unsignedтипу, це зручний спосіб сказати "запустіть цикл до нуля, а потім зупиніть".

Існують такі варіанти, як запуск jбільшого, ніж ви хочете, і навіть використання оператора слайдів for (std::size_t j = M + 1; j --> 0; ){ , які, безперечно, зрозуміліші, хоча вимагають більше введення тексту. Я думаю, що одним недоліком (крім здивовального ефекту, який він виробляє під час першої перевірки) є те, що він погано портує на мови без непідписаних типів, таких як Java.

Також зауважте, що схема, яку вибрав ваш начальник, "запозичує" можливе значення з unsignedнабору: у цьому випадку трапляється, що Mнабір std::numeric_limits<std::size_t>::max()не матиме правильної поведінки. Насправді в цьому випадку цикл нескінченний . (Це те, що ви спостерігаєте?) Вам слід вставити коментар з цього приводу в код і, можливо, навіть заявити про цю конкретну умову.


1 За умови Mвідсутності std::numeric_limits<std::size_t>::max().


7
Я звинувачую погоду.
Hatted Rooster

3
Якщо M дорівнює, std::numeric_limits<std::size_t>::max()тоді M + 1буде нулем, а for (std::size_t j = M + 1; j --> 0; )цикл взагалі не буде циклічним.
Піт Кіркхем,

51
Не називайте це "оператором слайдів". У С ++ такого оператора немає, це просто розумна на вигляд затуманення.
Ви

26
@DimitarMirchev: Тому що це не оператор. Це два оператори з непарним інтервалом. Назвіть це ідіомою, якщо ви наполягаєте на тому, щоб задавати інтерв'юйованим нерелевантні запитання (але в ідеалі запитайте їх, як вони реалізовуватимуть цю функцію, а не запитувати про "розумний" синтаксис).
Ви

1
Якщо припустити, що size_tце 64 біти, знадобиться кілька сотень років, щоб спостерігати некоректну поведінку в крайньому випадку. (За винятком випадків, коли оптимізатор може позбутися циклу.)
Carsten S

27

Те, що, напевно, намагався зробити ваш начальник, це зворотний відлік Mдо нуля включно, виконуючи певні дії з кожним номером.

На жаль, існує такий крайній випадок, коли це справді дасть вам нескінченний цикл, де Mє максимальне size_tзначення, яке ви можете мати. І, хоча добре визначено, що значення без підпису буде робити, коли ви зменшуєте його з нуля, я стверджую, що сам код є прикладом недбалого мислення, тим більше, що існує цілком життєздатне рішення без недоліків спроб ваших босів.

Цей безпечніший варіант (і більш читабельний, на мій погляд, при збереженні жорсткого обмеження обсягу), буде:

{
    std::size_t j = M;
    do {
        doSomethingWith(j);
    } while (j-- != 0);
}

Для прикладу див. Наступний код:

#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
    uint32_t quant = 0;
    unsigned short us = USHRT_MAX;
    std::cout << "Starting at " << us;
    do {
        quant++;
    } while (us-- != 0);
    std::cout << ", we would loop " << quant << " times.\n";
    return 0;
}

Це робить в основному те саме, що і, unsigned shortі ви можете бачити, що він обробляє кожне окреме значення:

Starting at 65535, we would loop 65536 times.

Заміна do..whileциклу у наведеному вище коді тим, що в основному робив ваш бос, призведе до нескінченного циклу. Спробуйте і подивіться:

for (unsigned int us2 = us; us2 <= us; --us2) {
    quant++;
}

7
Тепер крайній випадок є 0. Чому ні for(size_t j = M; j-- != 0; )?
LogicStuff

Так, це страждає від того, що кутовий випадок, можливо, частіше, ніж numeric_limits<size_t>::max().
Вірсавія,

7
@Logic et al, у методі, який я надав, немає регістру, я додав код, щоб показати це. З вашим намічуваним розчином, початкове значення M == 0призведе до елементу-не обробляються, тому він робить є крайній випадок. Використання мого do..whileметоду пост-перевірки взагалі виключає крайовий регістр. Якщо спробувати M == 1, ви побачите, що він робить і 1, і 0. Аналогічно, почніть з max_size_t(що б там не було), і він успішно почнеться в той момент, а потім зменшиться до нуля включно.
paxdiablo

Цікаво, що цей випадок мотивує використання того, що деякі сказали б, „неструктурований код”, оскільки він використовує „ exitif”, а саме { j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }. Один не може навіть потрібно замінити breakз , gotoякщо речами отримати вкладені, так як C має не названі петлі. Якщо "структурований" означає "сприйнятливий до міркувань про шматки", він структурований (макет допомагає!), А також якщо означає "сприйнятливий до формальної перевірки шляхом міркувань щодо попередніх та післяумов". Хоча в цьому випадку це j--працює, стиль існування може бути виправданим, коли потрібен більш складний перехід.
PJTraill

@PJTraill Я не можу сказати, що ви говорите структуровано, а те, що ви говорите, не структуровано. Міркування про те, як робити, просто - просто. Він не "використовує" вихід ні більше, ні більш неструктурованим способом, як це робить.
Філіпсія
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.