Оператор if внутри цикла while с тем же условием

Есть ли лучший способ написать следующий код, исключив повторяющееся условие в операторе if на C?

while (n < 0) {
   printf("Enter a positive integer: ");
   scanf("%d", &n);

   if (n < 0) {
      printf("Error: please enter a positive integer\n");
   }
}

Спасибо.

Что вы имеете в виду под словом «лучше»? Безопаснее? Быстрее? Более читабельный? Кроме того, какой тип n?

Lundin 31.10.2018 12:34

Во-первых, было бы лучше проверить возвращаемое значение scanf.

Steve Summit 31.10.2018 12:36

Возможно ли для n >= 0 до шлейфа while? Если нет, вы можете превратить его в «бесконечный» цикл и добавить break внутри блока if.

Ian Abbott 31.10.2018 12:36

n - это int. Под «лучше» я подразумеваю устранение повторяющегося условия в операторе if.

user10480767 31.10.2018 12:36

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

Ian Abbott 31.10.2018 12:41

@EricPostpischil Ну хотя бы для шлейфа while! :)

Ian Abbott 31.10.2018 12:43
0
6
1 003
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Просто переработайте разрыв цикла, когда будет введен правильный ввод. Таким образом, проверка выполняется только один раз:

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 { в одной строке): я не уверен, почему вы утверждаете, что вам не следует его использовать.

Bathsheba 31.10.2018 14:39

Это то, что 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 - Unslander Monica 31.10.2018 15:33

@StoryTeller: Моя проблема с goto связана как с тем фактом, что он пропускает метку в единицу перевода, так и с эффектом времени выполнения. То, как я это делаю, обходит первое. А switch (0) do - явный сигнал какой-то фанковой идиомы.

Bathsheba 31.10.2018 15:34

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

StoryTeller - Unslander Monica 31.10.2018 15:35

@StoryTeller: В некотором смысле я счастлив, что не знал этого. Всегда ли так было? Я использовал раннюю версию C очень в университете.

Bathsheba 31.10.2018 15:35

Кажется, столько же лет, сколько C89. Я не знаю, было ли так в K&R.

StoryTeller - Unslander Monica 31.10.2018 15:37

Другие вопросы по теме