scanf, fgets, fgetc пропускаются внутри цикла

Я пытаюсь сделать рекурсивное меню.

Эта программа позже будет работать с деревом (hojanodo), поэтому я слежу за корнем.

Проблема: по какой-то причине fgets/fgetc пропускается внутри рекурсивности при втором запуске, почему это происходит? Я хочу, чтобы пользователь вводил либо 1,2, либо 3. (целое число). Что можно исправить? и это лучший способ реализовать меню?

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

    #include<stdio.h>
#include<stdlib.h>

typedef struct node{
  char ch;
  int i;
  struct node *left;
  struct node *right;
}hojaNodo;


int  handle_menu(int eventHandler, hojaNodo **root);
int opcion_menu();
char get_symbol();
int get_userMenuInput();
int intro();

int main(){

  hojaNodo *treeRoot = NULL;
  intro();
  // system("clear");
  handle_menu(opcion_menu(), &treeRoot);

  return 0;
}

int opcion_menu(){
  int userOption;   

  printf("1.Agrega un Simbolo.\n");
  printf("2.Listar Codigo\n");
  printf("3.Exit");

    userOption = get_userMenuInput();
    printf("User: %d",userOption);

  if(userOption < 4 && userOption > 0){
    return userOption;
  }
  else
    return -1;

}//eof opcion_menu

int handle_menu(int userOption,hojaNodo **root){
  hojaNodo *tempRoot = NULL;
  tempRoot = *root;
   int valor;
   char simbol;

  switch(userOption){
  case 1:

    simbol = get_symbol();
    printf("Simbol: %c", simbol);

    break;
  case 2:
     printf("List Nodes\n");
    break;
  case 3:
    printf("Exit");
    userOption = -1;
    // destroy_tree(root);
    break;
    default:
    printf("userOption Error, Bye!");
    break;
  }//eof switch
  if(userOption != -1)
  handle_menu(opcion_menu(),&tempRoot);
  // return userOption;
  return -1;
}//eof menu()



char get_symbol(){

    /*char userKey[3]
    fgets(userKey,len,stdin);*/
    char simbolo;
    printf("Give me a symbol.");
    simbolo = fgetc(stdin);
    return simbolo;
}

int get_userMenuInput(){

     char userKey[3];
     int userOption;
    size_t len;
     len = sizeof(userKey);
     fgets(userKey,len,stdin);
     userOption = atoi(userKey);
  //printf("User Option: %d\n", userOption);
  return userOption;
}

person Goma    schedule 25.03.2015    source источник
comment
эта строка: 'switch(userOption){' использует userOption, однако для этой переменной не задано какое-либо конкретное значение (переменная объявлена ​​в main(), но ей не присвоено какое-либо конкретное значение, поэтому она содержит все, что когда-либо попадало на стек, в его местоположении в main () Кажется, что переменная должна быть инициализирована до 1.   -  person user3629249    schedule 25.03.2015
comment
всегда проверяйте возвращаемое значение из fgets() и семейства, чтобы убедиться, что операция прошла успешно   -  person user3629249    schedule 25.03.2015
comment
вызов fgets() вернет только два байта, если все, что вводит пользователь, является новой строкой. (и даже в системе Windows/DOS он вернет 3 символа.) предложите сделать переменную userkey несколько более длинным массивом, при вызове fgets() вторым параметром должен быть 'sizeof userKey'', поэтому модификации userkey должны быть только сделано в одном месте в коде. настоятельно рекомендуем исключить обработчик событий основной переменной и изменить вызов handle_menu на: handle_menu(opcion_menu(), &tempRoot);   -  person user3629249    schedule 25.03.2015
comment
main() всегда объявляется возвращающим int, поэтому ему нужно «возвращаемое значение»; оператор непосредственно перед последней закрывающей скобкой. предложите компилировать со всеми включенными предупреждениями, чтобы компилятор мог сообщить вам о проблемах, которые он видит в коде. Всегда исправлять предупреждения. они здесь, чтобы сообщить вам, что компилятор считает, что код имеет сомнительный синтаксис/логику   -  person user3629249    schedule 25.03.2015
comment
блок кода switch() должен находиться в цикле, который запрашивает и вводит пользовательский ввод до тех пор, пока не будет введено допустимое значение ( 1, 2, 3, -1). если рекурсия очень глубокая, сообщение "userOption Error, Bye!" будет выводиться для каждого уровня рекурсии. Примечание. Пользователь не может ввести «-1» в массив символов длиной всего 2 байта с помощью вызова fgets(). fgets прочитает один байт, добавит '\0' и вернется. поэтому все, что будет в массиве, будет '-','\0'   -  person user3629249    schedule 25.03.2015
comment
переменная 'valor', которая не определена, обычно содержит 0, потому что ключей, отличных от 0...9, намного больше, чем ключей 0..9.   -  person user3629249    schedule 25.03.2015
comment
@user3629249 user3629249 Я внес некоторые изменения в код, избавился от event_handler, добавил возврат в main, увеличил userKey. Что еще, по вашему мнению, мне нужно изменить?   -  person Goma    schedule 25.03.2015


Ответы (2)


Помимо всех комментариев, связанных с рекурсией и другими предлагаемыми изменениями, пожалуйста, ознакомьтесь с этим. Функция fgets() требует сброса входного потока. Это можно сделать с помощью fflush() или fgetc().

Простым решением будет:

В функции:

int opcion_menu () {

...
fgets(userKey,2,stdin);
fgetc(stdin); // Add this statement

Также в функции:

int handle_menu (int userOption, hojaNodo ** root)

            case 1:
                    printf("Give me a choice : ");
                    fgets(userKey,2,stdin);
                    fgetc(stdin); // add this statement

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

fgetc(stdin) удалит эти дополнительные символы новой строки.

person Anshul    schedule 25.03.2015
comment
Идея состоит в том, что пользователь вводит 1,2 или 3. Я думал, что это должно быть userKey= fgetc(stdin) /// также, если это число, которое мне нужно, как мне изменить char на int? Спасибо за вашу помощь - person Goma; 25.03.2015
comment
вместо этого вы можете использовать scanf. scanf(%d, &выбор); это автоматически преобразует ввод в целое число. - person Anshul; 25.03.2015
comment
Первоначально я начал со scanf, но моя проблема, которую вы видите, заключается в том, что использование рекурсивности для меню заставляет терминал пропускать 2+ scanf.... (один из вопросов..) - person Goma; 25.03.2015
comment
проверить мои модификации, которые оправдывают необходимость сброса данных fgetc(stdin), чтобы сбросить лишние символы, а не для ввода по выбору - person Anshul; 25.03.2015
comment
Сделал некоторые изменения в моем коде, я запустил его, но когда я захожу в меню во второй раз, я getuserOption = '0'...... - person Goma; 25.03.2015

Я не знаю, может ли это кому-нибудь помочь. В моем случае мне пришлось «освободить» буфер от char с помощью этой функции:

void clean(){
char cTemp;
while((cTemp = getchar()) !=  '\n')
;
}

Я не совсем уверен, почему это работает, но это так (если кто-то это сделает, пожалуйста, добавьте это в мой ответ). Я вызываю его прямо перед вызовом get_userOption();

person Goma    schedule 25.03.2015