Использование итератора цикла 'for' после того, как цикл существует в C
В течение многих лет я привык к тому, что не использовал значение тега цикла for
после выхода цикла. Я мог бы поклясться, что я это сделал, потому что он использовал для создания предупреждения о компиляторе, но после того, как мне бросили вызов в недавнем обзоре кода, я оказался ошибочным.
Например, я всегда это делал (ПРИМЕЧАНИЕ: наши стандарты кода запрещают использование ключевого слова "break" ):
int i, result;
bool done = false;
for (i=0; i<10 && !done; i++) {
if (some_condition) {
result = i;
done = true;
}
}
// Value of i may be undefined here
Теперь, очевидно, переменная result
может быть удалена, если я могу положиться на значение i. Я думал, что из-за оптимизации компилятора вы не можете полагаться на значение итератора цикла. Я просто помню обучение phantom? Или это стандарт (в частности, относительно GNU C)?
Ответы
Ответ 1
Нет ничего плохого в C89, C99, или C11, чтобы получить доступ к переменной итерации после инструкции for
.
int i;
for (i = 0; i < 10; i++) {
/* Some code */
}
printf("%d\n", i); // No magic, the value is 10
В C99 вы также можете использовать объявление как первое предложение оператора for
, и в этом случае, конечно, объявленная переменная не может использоваться после оператора for
.
Ответ 2
Различные языки имеют разные правила. В Pascal компилятору разрешено оптимизировать хранение индекса цикла после последнего приращения, поэтому оно может быть первым значением завершения цикла или последним допустимым значением.
Ответ 3
Существует множество случаев использования, когда цикл for используется только для продвижения итератора. Это можно увидеть в некоторых реализациях strlen (хотя, по общему признанию, есть и другие способы сделать strlen) и других видов функций, целью которых является найти определенный предел:
/*find the index of the first element which is odd*/
for (ii = 0; ii < nelem && arry[ii] % 2 == 0; ii++);
Как уже упоминалось, точка путаницы может исходить от конструкций, в которых сам итератор определен в инструкции for.
В целом для высказываний очень сильно, и, к сожалению, они обычно никогда не используются в полной мере.
Например, другая версия того же цикла может быть записана следующим образом (хотя это не продемонстрировало бы безопасность использования итератора):
#include <stdio.h>
int main(void)
{
int cur, ii = 0, nelem, arry [] = { 1, 2, 4, 6, 8, 8, 3, 42, 45, 67 };
int sum = 0;
nelem = sizeof(arry) / sizeof(int);
/* Look mom! no curly braces! */
for (
ii = 0;
ii < nelem && ((cur = arry[ii]) %2 == 0 ||
((printf("Found odd number: %d\n", cur)||1)));
ii++, sum += cur
);
printf("Sum of all numbers is %d\n", sum);
return 0;
}
В этом конкретном случае для этой конкретной задачи, похоже, много работы, но для некоторых вещей это может быть очень удобно.
Ответ 4
Несмотря на то, что значение этой переменной управления циклом for
хорошо определено, вам, возможно, сказали, что следует избегать использования переменной управления циклом for
после цикла for из-за того, как обрабатывается область видимости этой переменной и особенно потому, что обработка изменила историю С++ (я знаю, что этот вопрос отмечен как "C", но я думаю, что обоснование для избежания использования переменной управления циклом после цикла может иметь происхождение в этой истории С++).
Например, рассмотрим следующий код:
int more_work_to_do(void)
{
return 1;
}
int some_condition(void)
{
return 1;
}
int foo()
{
int i = 100;
while (more_work_to_do()) {
int done = 0;
for (int i = 0; i < 10 && !done; i++) {
if (some_condition()) {
done = 1;
}
}
if (done) return i; // which `i`?
}
return 1;
}
При некоторых старых правилах определения для i
, объявленных в цикле for
, значение, возвращаемое в выражении с комментарием, "которое i
" будет определяться циклом for
(VС++ 6 использует эти правила). В соответствии с новыми стандартными правилами для определения этой переменной возвращается значение i
, объявленное в начале функции.
Ответ 5
Пока я не знаю, как появилась ваша привычка, я могу рассказать вам, как моя привычка делать то же самое. Это выглядело следующим образом:
for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
if (someCondition) found = TRUE;
}
foundIndex = i-1;
В принципе, код, подобный этому, записывается, когда ключевое слово break не разрешено некоторыми правилами кодирования, например. на основе MISRA. Если вы не выходите из цикла, цикл, как правило, оставляет вас с "i", который отключен одним из того, что вам нужно.
Иногда вы можете даже найти это:
for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
if (someCondition) found = TRUE;
}
foundIndex = i;
Это просто семантически неправильно и может быть найдено, когда "запретить правило ключевого слова" вводится в существующую базу кода, которая недостаточно покрыта модульными тестами. Может показаться удивительным, но все это там...