0

I'm having some trouble with getchar(), in particular I have a char which, in a while loop, get the value returned from getchar() but I want to take just the first char and, if I insert a longer string (like "aaawssdawa"), I still want just the first char. My code, instead, processes the entire string.

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

#define ROWS 20
#define COLUMNS 65

char grid[ROWS][COLUMNS];
int score = 0;
void fillGridInitializer();
void printGrid();
void start();
void printScore();
void printAll();
int main(int argc, char *argv[]) {
  start();
  return 0;
}
void start() {
  char movement;
  int riga = 0, colonna = 0;
  fillGridInitializer();
  grid[riga][colonna] = '#';
  system("clear");
  printAll();
  while (1) {
    movement = getchar();
    switch (movement) {
    case 'w':
      if ((riga - 1) >= 0) {
        grid[riga][colonna] = '-';
        riga = riga - 1;
      }
      break;
    case 's':
      if ((riga + 1) < ROWS) {
        grid[riga][colonna] = '-';
        riga = riga + 1;
      }
      break;
    case 'a':
      if ((colonna - 1) >= 0) {
        grid[riga][colonna] = '-';
        colonna = (colonna - 1) % COLUMNS;
      }
      break;
    case 'd':
      if ((colonna + 1) < COLUMNS) {
        grid[riga][colonna] = '-';
        colonna = (colonna + 1) % COLUMNS;
      }
      break;
    default:
      break;
    }
    if (movement == 'p') {
      printf("+++++Game Over+++++\n\n");
      break;
    }
    system("clear");
    grid[riga][colonna] = '#';
    printAll();
  }
}

void printAll() {
  printScore();
  printGrid();
}
void fillGridInitializer() {
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++) {
    for (j = 0; j < COLUMNS; j++) {
      grid[i][j] = '-';
    }
  }
}
void printScore() { printf("\t SCORE: %d\n", score); }
void printGrid() {
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++) {
    printf("\t");
    for (j = 0; j < COLUMNS; j++) {
      printf("%c", grid[i][j]);
    }
    printf("\n");
  }
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • After you read a character, you need to ignore all the characters until the next newline. – Barmar Jan 23 '20 at 17:07
  • OT: regarding; `int main(int argc, char *argv[]) {` Since the parameters are not used, the compiler will output two warning messages about unused parameters. To eliminate those warning messages, suggest using the other valid signature for `main()` I.E. `int main( void )` – user3629249 Jan 24 '20 at 11:47
  • OT: for ease of readabillity and understaning 1) separate functions by 2 or 3 blank lines (be consistent) 2) separate code blocks: `for` `if` `else` `while` `do...while` `switch` `case` `default` via a single blank line. 3) consistently indent the code. Indent after every opening brace '{'. Unindent before every closing brace '}'. Suggest each indent level be 4 spaces. 4) follow the axiom: *only one statement per line and (at most) one variable declaration per statement.* – user3629249 Jan 24 '20 at 11:51
  • OT: regarding: `void fillGridInitializer(); void printGrid(); void start(); void printScore(); void printAll();` and similar prototype statements, where the function takes no parameters, then in the prototype include a `void` inside the parens, similar to: `void fillGridInitializer( void ); void printGrid( void ); void start( void ); void printScore( void ); void printAll( void );` Otherwise the compiler will generate code for the function to take any number of parameters rather than only no parameters – user3629249 Jan 24 '20 at 11:58

2 Answers2

2

After you process the first character, call getchar() in a loop until you get a newline or EOF.

Also, getchar() returns int, you should declare the variable accordingly, so you can compare with EOF properly.

void start() {
  int movement;
  int riga = 0, colonna = 0;
  fillGridInitializer();
  grid[riga][colonna] = '#';
  system("clear");
  printAll();
  while (1) {
    movement = getchar();
    if (movement == EOF) {
        break;
    }
    switch (movement) {
    case 'w':
      if ((riga - 1) >= 0) {
        grid[riga][colonna] = '-';
        riga = riga - 1;
      }
      break;
    case 's':
      if ((riga + 1) < ROWS) {
        grid[riga][colonna] = '-';
        riga = riga + 1;
      }
      break;
    case 'a':
      if ((colonna - 1) >= 0) {
        grid[riga][colonna] = '-';
        colonna = (colonna - 1) % COLUMNS;
      }
      break;
    case 'd':
      if ((colonna + 1) < COLUMNS) {
        grid[riga][colonna] = '-';
        colonna = (colonna + 1) % COLUMNS;
      }
      break;
    default:
      break;
    }
    if (movement == 'p') {
      printf("+++++Game Over+++++\n\n");
      break;
    }
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {} # ignore the rest of the line
    if (ch == EOF) {
        break;
    }
    system("clear");
    grid[riga][colonna] = '#';
    printAll();
  }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks, it worked! Also, how can getchar return EOF? how can i get EOF from stdin? – Mario Turco Jan 23 '20 at 17:19
  • If you redirect stdin to a file, it gets EOF when it reaches the end of the file. And on the terminal you can type Ctl-d on Unix or Ctl-z on Windows to send EOF. – Barmar Jan 23 '20 at 17:20
0

Assuming you just want to read a char and discard everything else in the same line, you can do the following:

Just read a line and only save the first char.

But, you shouldn't use this as it it vulnerable to buffer overflows(read what comes after it).

char read(){
    char buf[BUF_SIZE];
    gets(buf);
    return buf[0];
}

But you shouldn't use gets because of possible buffer overflows.

In order to prevent that, you could use fgets(see this):

char read(){
    char buf[BUF_SIZE];
    fgets(buf,BUF_SIZE,stdin);
    return buf[0];
}

In both cases, BUF_SIZE is a constant defining the maximum length of the string. The second example is buffer overflow protected but it will only work if the line ends before the buffer ends. You can just call fgets multiple times in order to bypass this issue:

char read(){
    char buf[BUF_SIZE];
    do{
        fgets(buf, BUF_SIZE,stdin);
    }while(buf[strlen(buf)]!='\n');
    return buf[0];
}

Note that the last method needs <string.h>.

Integrated in your code, it would be something like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define ROWS 20
#define COLUMNS 65
#define BUF_SIZE 255

char grid[ROWS][COLUMNS];
int score = 0;
void fillGridInitializer();
void printGrid();
void start();
void printScore();
void printAll();
int main(int argc, char *argv[]) {
  start();
  return 0;
}
void start() {
  char movement;
  int riga = 0, colonna = 0;
  fillGridInitializer();
  grid[riga][colonna] = '#';
  system("clear");
  printAll();
  while (1) {
    movement = read();
    switch (movement) {
    case 'w':
      if ((riga - 1) >= 0) {
        grid[riga][colonna] = '-';
        riga = riga - 1;
      }
      break;
    case 's':
      if ((riga + 1) < ROWS) {
        grid[riga][colonna] = '-';
        riga = riga + 1;
      }
      break;
    case 'a':
      if ((colonna - 1) >= 0) {
        grid[riga][colonna] = '-';
        colonna = (colonna - 1) % COLUMNS;
      }
      break;
    case 'd':
      if ((colonna + 1) < COLUMNS) {
        grid[riga][colonna] = '-';
        colonna = (colonna + 1) % COLUMNS;
      }
      break;
    default:
      break;
    }
    if (movement == 'p') {
      printf("+++++Game Over+++++\n\n");
      break;
    }
    system("clear");
    grid[riga][colonna] = '#';
    printAll();
  }
}

void printAll() {
  printScore();
  printGrid();
}
void fillGridInitializer() {
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++) {
    for (j = 0; j < COLUMNS; j++) {
      grid[i][j] = '-';
    }
  }
}
void printScore() { printf("\t SCORE: %d\n", score); }
void printGrid() {
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++) {
    printf("\t");
    for (j = 0; j < COLUMNS; j++) {
      printf("%c", grid[i][j]);
    }
    printf("\n");
  }
}
char read(){
    char buf[BUF_SIZE];
    do{
        fgets(buf, BUF_SIZE,stdin);
    }while(buf[strlen(buf)]!='\n');
    return buf[0];
}
dan1st
  • 12,568
  • 8
  • 34
  • 67
  • Don't even mention `gets()`. It has been removed from the language, there's no need to suggest it just to turn around and say don't use it. – Barmar Jan 23 '20 at 17:21
  • I added it just to be complete. I've edited my answer. – dan1st Jan 23 '20 at 17:23