Есть ли лучший способ написать следующий код, исключив повторяющееся условие в операторе if
на C?
while (n < 0) {
printf("Enter a positive integer: ");
scanf("%d", &n);
if (n < 0) {
printf("Error: please enter a positive integer\n");
}
}
Спасибо.
Во-первых, было бы лучше проверить возвращаемое значение scanf
.
Возможно ли для n >= 0
до шлейфа while
? Если нет, вы можете превратить его в «бесконечный» цикл и добавить break
внутри блока if
.
n - это int. Под «лучше» я подразумеваю устранение повторяющегося условия в операторе if.
Когда оптимизация включена, достойный компилятор должен оптимизировать повторяющееся условие, поэтому, вероятно, не стоит беспокоиться. Единственный другой способ убрать повторяющееся условие - выйти из бесконечного цикла.
@EricPostpischil Ну хотя бы для шлейфа while
! :)
Просто переработайте разрыв цикла, когда будет введен правильный ввод. Таким образом, проверка выполняется только один раз:
while (1)
{
printf("Enter a positive integer: ");
scanf("%d", &n);
if (n >= 0)
break;
printf("Error: please enter a positive integer\n");
}
И, как указано в комментариях, оптимизированный компилятор должен иметь возможность полностью изменить цикл.
Следующие ниже примеры представлены в том духе, что люди должны знать, что доступно на языке. 1 То, как я обычно пишу код, показано в Ответ Frankie_C. Как отмечали некоторые, оптимизация обычно делает этот простой случай не заслуживающим беспокойства, но вопрос не ограничивается простой оценкой, такой как n < 0
; тест может быть вызовом функции для какой-то дорогостоящей оценки более сложных критериев.
Людям это не понравится, но:
goto middle;
do
{
printf("Error, please enter a positive integer\n");
middle:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n < 0);
Если вы категорически против goto
, вы можете использовать урезанную версию Устройство Даффа:
switch (0)
do
{
printf("Error, please enter a positive integer\n");
case 0:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n < 0);
Но не стоит.
1 Обычно программистам приходится работать с кодом, написанным другими, поэтому они должны быть готовы распознать и понять все, что можно выразить на языке, даже если это всего лишь первый шаг к переписыванию его в более совершенный код. И иногда возникают ситуации, когда «некрасивый» код становится желательным по коммерческим или другим практическим причинам.
Повышен. Я довольно часто использую второй способ (хотя форматирую его несколько иначе; я пишу switch (0) do {
в одной строке): я не уверен, почему вы утверждаете, что вам не следует его использовать.
Это то, что IMO лучше всего выполнить с небольшим рефакторингом:
#include <stdio.h>
#include <stdbool.h>
static bool get_postive_integer(int *pOut) {
int n;
printf("Enter a positive integer: ");
scanf("%d", &n);
if (n < 0)
return false;
*pOut = n;
return true;
}
int main(void)
{
int n;
while (!get_postive_integer(&n)) {
printf("Error: please enter a positive integer\n");
}
}
Задайте операции имя, убедитесь, что она не выполняется, и только затем распечатайте соответствующее сообщение. Условие успеха или неудачи кодируется здесь только один раз в названной операции.
Вы можете использовать:
while (printf("Enter a positive integer: ") > 0 &&
scanf("%d", &n) == 1 &&
n < 0)
{
printf("Error: please enter a positive integer\n");
}
Это останавливается, если printf()
выходит из строя, если scanf()
выходит из строя или если значение в n
неотрицательно. Рекомендуется всегда проверять, что scanf()
работает успешно. Просто удобно, что printf()
возвращает количество записанных символов (или отрицательное число в случае сбоя), поэтому его также можно использовать в условии. Вы также можете добавить fflush(stdout) == 0 &&
в стек операций.
Или вы можете решить, что код в условии должен быть в функции:
static int read_positive_integer(void)
{
int value;
if (printf("Enter a positive integer: ") > 0 &&
fflush(stdout) == 0 &&
scanf("%d", &value) == 1 &&
value >= 0)
return value;
return -1;
}
а затем вызывающий код:
while ((n = read_positive_integer()) < 0)
printf("Error: please enter a positive integer\n");
Есть много вариаций на эту тему; вы можете превратить цикл while
в функцию; вы можете превратить подсказки в параметры функции. Вы можете решить быть более осторожными при сообщении о том, что идет не так (различные действия в случае сбоя printf()
по сравнению с тем, что происходит, если scanf()
возвращает 0 (нечисловые данные на входе) или EOF (во входных данных больше нет данных).
Другой вариант - разделить на функцию:
int func(){
int n;
printf("Enter a positive integer: ");
scanf("%d", &n);
return scanf("%d", &n) == 1 ? n : -1;
}
и цикл становится
while ((n = func()) < 0){
printf("Error: please enter a positive integer\n");
}
хотя задание в проверке состояния не всем по вкусу. Обратите внимание, что я возвращаю -1, если возвращаемое значение scanf
не равно 1, что вы всегда должны проверять.
Что я делаю в этой ситуации (см. Ответ Эрика), так это пишу
switch (0) do {
printf("Error, please enter a positive integer\n");
case 0:
printf("Enter a positive integer: ");
scanf("%d", &n);
} while (n/*ToDo - make sure n is initialised if scanf fails*/ < 0);
Знаешь ... Эрик честно говорит об использовании goto
. Это здесь просто под видом чего-то структурированного.
@StoryTeller: Моя проблема с goto
связана как с тем фактом, что он пропускает метку в единицу перевода, так и с эффектом времени выполнения. То, как я это делаю, обходит первое. А switch (0) do
- явный сигнал какой-то фанковой идиомы.
Не в единицу перевода, а в функцию. Ярлыки имеют область действия, поэтому они не будут протекать в глобальном масштабе.
@StoryTeller: В некотором смысле я счастлив, что не знал этого. Всегда ли так было? Я использовал раннюю версию C очень в университете.
Кажется, столько же лет, сколько C89. Я не знаю, было ли так в K&R.
Что вы имеете в виду под словом «лучше»? Безопаснее? Быстрее? Более читабельный? Кроме того, какой тип
n
?