Справка по коду C (не удается найти причину, вызывающую проблему)

#c

#c

Вопрос:

Приветствую, короче говоря, ниже приведен код

Код

 // This program would sum up all the integer provided by user

#include <stdio.h>

int main(void)
{
 int sum = 0;                  // initialize and declare variable
 int num;
 char check;
 int status;

 printf("Please enter an integer to be summed :"); // prompt user for an integer
 status = scanf("%d" , amp;num);                                

  while(status == 1)                              //If user enter an integer , while loop test condition is true
  {
    sum = sum   num;                           //sum the input integer
    printf("Do you wanna add more integer?(y/n) :");   //Asking for user next action
    scanf("%c" , amp;check);

          if(check == 'y')                   //Check user's answer

        status = scanf("%d" , amp;num);

    else

        status = 0;

  }


 return 0;
}
  

Проблема

Первое, что я делаю при запуске программы, — это указываю целое число, после чего она просто распечатывает управляющую строку Do you wanna add more integer?(y/n) : .Тогда проблема возникает, вместо того, чтобы ждать, пока я наберу y или n , программа просто завершит работу сама, что означает, что в командной строке Windows она выдаст мне строку Press any key to continue.... .

Я продолжаю читать код построчно, чтобы найти какую-либо семантическую ошибку (предположим, что синтаксической ошибки нет, поскольку компилятор пожаловался бы, если бы она была) или любую логическую ошибку, которую я допустил, но безрезультатно.Итак, я подозреваю, что существуют какие-либо правила C, в которых упоминается, что мы не должны запрашивать use if() с символом в качестве тестового значения, когда оно заключено в while() цикл, или любую другую вещь, которой я пренебрег??

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

Комментарии:

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

2. Пожалуйста, игнорируйте всех, кто говорит вам использовать fflush (stdin), они не знают, о чем говорят. Проверьте этот C FAQ и этот C FAQ .

Ответ №1:

Вы используете scanf() . Мое общее правило таково: не делайте этого.

Проблема в том, что scanf() практически все, что угодно, оставит символы после прочитанного вами значения (обычно новой строки) в буфере, за исключением того, что %c считывает следующий символ независимо от того, что это такое. Итак, вы получаете там новую строку, а затем ответ y / n передается следующему scanf() .

Я предпочитаю читать строки, а затем sscanf() извлекать из них; таким образом, я точно знаю, что я получаю.

Комментарии:

1. Хе-хе, лучший совет здесь: просто не используйте scanf() 🙂 Хотя, вы должны были также упомянуть fgets() наряду с sscanf() .

Ответ №2:

Ставьте getchar() после каждого сканирования, чтобы очистить буфер ввода stdin от символа перевода строки в конце ‘ n’, который добавляется в буфер, когда пользователь нажимает enter.

Ответ №3:

Вы вводите число, а затем нажимаете клавишу ввода. "%d" Строка формата будет считываться до первого целого числа и не будет считывать введенный вами символ новой строки. Этот символ новой строки остался бы внутри буфера, который считывается первым scanf в цикле со "%c" спецификатором. Поэтому check == 'y' становится ложным, поскольку check содержит 0x0a , то есть эквивалент новой строки ASCII, после того, как он прочитал новую строку.

Обратите внимание, что, если вы хотите, чтобы именно ваш фрагмент кода работал, вам нужно ввести цифры и параметры y / n один за другим без перевода строки ( 0x0a ), пробела ( 0x20 ) или других разделителей. Например, если вы хотите подвести итог по списку 1,2,3,4,5, то вам нужно ввести его следующим образом в свой код: 1y2y3y4y5n . Если вы введете printf инструкцию, печатающую status значение после scanf запроса yes / no, вы сможете четко увидеть, что последний непрочитанный символ считывается "%c" .

Если вы хотите ввести числа и опции разделенными, то вам нужно использовать дополнительный символ. Что вы можете сделать, используя getchar () или используя оператор подавления аргументов в строке формата: scanf ("%*c%c", amp;check) . Это "%*c" считает один символ из stdin , а затем просто отбрасывает его, и затем "%c" получит ваш ввод в check переменную. Но если вы введете дополнительную новую строку или, скажем, у вас будет несколько завершающих пробелов перед введенными вами параметрами или после введенного вами числа, то это приведет к тому, что только один из них будет использован вышеупомянутым и отброшен, и возникнет та же проблема.

Вы также можете использовать строку scanf (" %c", amp;check); the " %c" (обратите внимание на завершающий пробел), которая пропустит любое количество символов пробела, введенных вами перед вводом первого символа.

На самом деле это зависит от того, как вы хотите интерпретировать и обрабатывать входные данные, вам нужно настроить в соответствии с этим. Вероятно, сделать :

  char check[2];

 while (status == 1)
 {
    sum  = num;

    printf("Do you wanna add more integer?(y/n) :");   //Asking for user next action
    scanf("%s" , check);

    if ((check[0] == 'y')amp;amp;(check[1] == ''))    //Check user's answer  
       status = scanf("%d" , amp;num);
    else
       status = 0;
 }
  

или просто используйте strcmp (check, "y") == 0 или другие способы.

Ответ №4:

Вот отредактированный код, работающий у меня:

 #include <stdio.h>
#include <conio.h>
int main(void)
{

   int sum = 0;                  
   int num;
   char check;
   int status=1;

   printf("Please enter an integer to be summed :");
   status = scanf("%d" , amp;num);      
   while(status == 1)
   {
      sum = sum   num;                          
      printf("Do you wanna add more integer?(y/n) :");   
      fflush(stdin);
      check = getche();

      if(check == 'y')                   

         status = scanf("%d" , amp;num);
      else

         status = 0;
   }
    printf("nThe sum is: %d",sum);
    getch();
    return 0;
 }
  

Комментарии:

1. что такое conio.h ? это из TurboC 3.1?

2. -1 fflush(stdin) — это неопределенное поведение, и вы не должны учить новичков его использованию. Кроме того, в коде без необходимости используются нестандартные библиотеки.

Ответ №5:

состояние начинается как неопределенное. Вы хотите убедиться, что цикл ввода / суммирования выполняется хотя бы один раз, поэтому вам следует изначально установить для него значение 1. Таким образом, вы знаете, что он всегда будет запускаться с первого раза.

То, что @geekosaur сказал о scanf (), верно — это оставит вещи во входном буфере, что вызовет странное поведение для следующих инструкций ввода. Но использовать scanf() нормально, если вы очищаете stdin каждый раз, когда используете его. Если вы используете Windows, есть готовая функция:

 fflush(stdin);
  

Если вы используете другую операционную систему (или хотите, чтобы ваш код был переносимым), вы можете поискать аналогичную функцию для включения в вашу программу. Ввод на C — это всего лишь одна из ужасных вещей, с которыми начинающему программисту приходится работать (к этому привыкаешь). Я полагаю, что это также может помочь, если вы будете помещать пробелы в начало строки формата scanf () каждый раз, когда вы ее используете — например.

 scanf(" %d");
  

Это должно указать программе игнорировать любые пробелы, уже присутствующие во входных данных (включая новые строки).

Следует отметить, что здесь действительно есть 2 входных параметра состояния — int status и char check. Вы могли бы немного упростить для себя задачу, установив условие в качестве пользовательского ввода:

 char check = 'y';

...

while(check == 'y')
  

Это должно привести к тому же поведению, что и у вас сейчас. Вместо этого я использовал цикл do-while:

 // This program would sum up all the integer provided by user

#include <stdio.h>

int main(void)
{
    int sum = 0;
    int num;
    char check;

    do // a do-while loop always runs at least once - perfect for input validation
    {
        printf("Please enter an integer to be summed :");
        fflush(stdin); //only in Windows - remove for other OS
        scanf(" %d" , amp;num);
        sum  = num; //another way of writing sum = sum   num                                
        printf("Do you wanna add more integer?(y/n) :");
        fflush(stdin); //only in Windows - remove for other OS
        scanf(" %c" , amp;check);    
    }while(check == 'y');

    //if you want to print the sum...
    printf("The sum is %in", sum);
    getchar();

    return 0;
}
  

У меня это сработало в Windows с удаленными вызовами fflush ().

Комментарии:

1. -1 fflush(stdin) — это неопределенное поведение, и вы не должны учить новичков его использованию.

2. @Lundin это ваше мнение, и это прекрасно — но в настоящее время я изучаю вводный C (через пару часов у меня экзамен), и это то, чему меня научил лектор. Использовать. Итак, ваши взгляды не универсальны. Переносимость кода может быть или не быть одной из ваших целей при написании программы, и я достаточно четко изложил условия, при которых вы могли бы использовать fflush() (по крайней мере, так, как меня этому учили). Но спасибо, что поделились ссылками выше. Я действительно не вижу, как вызов getchar() более эффективен, чем включение пробела в начало строки формата scanf() — не могли бы вы просветить.

3. Мне грустно слышать, что у вас есть учитель, который не слишком хорошо знает C. Поскольку это не «считает», это даже не проблема переносимости. В стандарте C явно указано, что fflush (stdin) является неопределенным поведением (ISO 9899: 1999 7.19.5.2). И неопределенное поведение означает, что может произойти любая случайная вещь. При возникновении неопределенного поведения программа может свободно форматировать ваш C: или стрелять в невинных свидетелей.

4. @Lundin, Нет, если это явно определено для этой реализации — используете ли вы это тогда, возможно, проблема с переносимостью. Сколько компиляторов в любом случае полностью соответствуют стандарту 99? Я уверен, что есть лучшие способы обработки символов, оставшихся в stdin, но хотите ли вы познакомить с этими методами людей, которые только начинают изучать язык и программирование, — это то, что учитель может взвесить с обеих сторон. Не многие пишущие производственный код создают свои собственные функции синтаксического анализа для ввода — вы ожидаете, что это сделает новичок? Может быть, а может и нет. И мой экзамен прошел хорошо, спасибо, что спросили.

5. @thelionroars1337 То, о чем вы говорите, является расширением за пределами языка C, т. Е. компилятор изобретает какой-то собственный язык, который не является C. К сожалению, это очень распространено, компиляторы реализуют свою собственную чушь, даже когда для этого нет причин. Что касается fflush (stdin), то это было неопределенное поведение и в C90, оно не имеет ничего общего с C99.