2016-12-10 9 views
3

Так что я использую size_t вместо int в любом индексировании для цикла для предотвращения отрицательных индексов. Но при обратном отсчете это приводит к переполнению:Бесконечный цикл при использовании size_t в обратном отсчете для цикла

for (size_t i = 10; i >= 0; --i) { 
    // Do something, f.ex. array[i] = i 
} 

Что было бы хорошим способом предотвратить это?

  • Использование: int вместо этого?
  • Использование i == 0 в качестве условия завершения? (Это, однако, не сработает, если мне понадобится 0)

Буду признателен за любые отзывы!

+0

Это не технически переполнение, потому что 'size_t' является неподписанным типом, но это определенно бесконечный цикл, поскольку условие завершения всегда истинно. – chqrlie

ответ

1

Простейшим способом является увеличение верхнего значения. Например

const size_t N = 10; 

for (size_t i = N + 1; i != 0; --i) { 
    // Do something, f.ex. array[i-1] = i-1 
} 

или

const size_t N = 10; 

for (size_t i = N + 1; i-- != 0;) { 
    // Do something, f.ex. array[i] = i 
} 

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

#include <stdio.h> 

int main(void) 
{ 
    const size_t N = 10; 

    for (size_t i = N, j = N; !(i == 0 && j == -1); j--) 
    { 
     i = j; 
     printf("%zu ", i); 
    } 

    printf("\n"); 
} 

В противном случае вы можете использовать цикл do-while. В этом случае он более подходит. Например

size_t i = N; 

do 
{ 
    printf("%zu ", i); 
} while (i-- != 0); 
+0

Хорошо, вы побеждаете (на 4 секунды!) – wildplasser

+0

Но я не могу использовать 0 в этом направлении, не так ли? – AdHominem

+0

@AdHominem Почему вы не можете использовать 0? Начальное значение цикла 0 + 1. Таким образом, вы можете использовать 0. –

4
for (size_t i = 11; i-- > 0;) { 
    // Do something, f.ex. array[i] = i 
} 
+0

Вы уверены, что исходное значение верное? – Bathsheba

+0

Да. Quetion запускает цикл со значением = 10 (что странно, но не невозможно). Я начинаю с 11, но в первый раз, когда цикл * body * вводится, он уже уменьшен, – wildplasser

+0

Упс. Имейте upvote! – Bathsheba

2

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

for (size_t i = 10 + 1; i--> 0;) 

Это на самом деле не оператор, но это то, что стало известно как за эти годы.

+2

Я лично называю это оператором downto. – chqrlie

0

Проблема в том, что в вашей реализации size_t имеет тип unsigned long или unsigned int.

При i = 0 условие выполнено и -i преобразует его в 4294967295UL = ULONG_MAX, поэтому условие теста i >= 0 из цикла никогда не будет ложным.

size_t имеет некоторый неподписанный тип, я никогда не буду отрицательным.

3

Это не технически переполнение, потому что size_t является неподписанным типом, но это определенно бесконечный цикл, поскольку условие завершения всегда истинно.

Целые числа без знака обертывание при декременте: 0. Обратите внимание, что ваш цикл будет работать 11 раз, прежде чем произойдет обертка, а не 10.

Вы должны проверить условие перед уменьшением индекса. Запуск перечисления с начальным значением, превышающим максимально допустимый индекс, улучшает визуальную согласованность и упрощает тест.

Вот исправленный вариант, где вы можете увидеть, что начальное значение для i является число элементов массива:

int array[11]; 
for (size_t i = 11; i-- > 0;) { 
    // Do something, f.ex. array[i] = i 
} 
5
for (size_t i = 10; i <= 10; --i) // do something 

При переполнении этого произойдет, то округлить до наибольшего целого числа и, следовательно, условие не удастся.

+0

@ Начальник Да. Я просто копирую и добавляю часть 'i <= 10'. Спасибо за указание. –

+0

Новый способ управления циклом: UV, но он _looks_ встречный интуитивный. – chux

+0

Когда я был маленьким ножом, я делал это так, но получил выговор моим профессором. – Bathsheba

1

size_t i = 10; i >= 0; никогда не является ложным, так как size_t - это некоторый тип без знака, и все значения больше или равны нулю.

... size_t, который является целым числом без знака тип результата оператора sizeof; ...
C11 §7.19 2

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


Лучшая альтернатива зависит от кодирования целей

Хороший код позволяет избежать магических чисел, как этот голый 10. Лучше, если код, полученный этим. В этом простом случае, это должно было быть 11.

#define A_SIZE 11 
int array[A_SIZE]; 
... 
for (size_t i = A_SIZE; i-- > 0;) { 
    // Do something, f.ex. array[i] = i 
} 

OTOH, код, возможно, имел break условия в цикле и нуждается i в пожилом код для обозначения array[] использований

size_t i = A_SIZE; 
while (i > 0) { 
    if (...) break; 
    i--; 
    // Do something, f.ex. array[i] = i 
    if (...) break; 
} 
// Do something with i 

код может иметь требование контракта использовать 10 в разных местах.

// Contract says loop must handle indexes 0 to N, inclusive 
#define N 10 
int array[N + 1]; 

for (size_t i = N; i + 1 > 0; i--) { 
    // Do something, f.ex. array[i] = i 
} 

Хорошие оптимизирующие компиляторы не будет выполнять +1 на каждом i + 1 > 0, но создать эквивалентный эффективный код.

Код - это мода, которая наилучшим образом передает общее значение кода.

+1

Удивительно! Я никогда не думал о 'i + 1> 0', чтобы протестировать обертку. Это эквивалентно 'i! = SIZE_MAX', менее уродливому, но, возможно, более интригующему, поскольку он просит упрощения. Один случай не рассматривается: он не работает для 'N = SIZE_MAX', что довольно редко встречается в современных системах, но не было редкостью в дни 16-разрядного защищенного режима. Вы также можете использовать 'i! = -1', который имеет тот же недостаток. – chqrlie

+0

Выглядит неплохо. Я предполагаю, что на практике это было бы ошибкой, склонной к отключению, так как вы могли легко забыть добавить 1. Использование SIZE_MAX не намного более противоречиво, чем 'i <= n; --i' или 'i--> 0;', все они ожидают обертывания. – AdHominem