Korpel Korpel - 3 months ago 10
C Question

getc() "stores" the input and reuses it with scanf(), not allowing the user to input

I have the following piece of code. What the program does is ask the user for 2 strings. For the first one i tried out using a memmory allocated string with malloc() and used getc() in order to het the input from the user. For the second string i used an array of characters with specified size and scanf(). The issue i am having is that scanf gets the value that exceeded from the getc() used some lines of code before. How can i stop this behaviour? Also is

enaString[ctr] = '\0';
and
diaxwristiki[LINESIZE-1] = '\0'
considered undefined behaviour? Or this is the right way of adding the null terminating character into your string?

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


int main(int argc, char* argv[])
{
int LINESIZE = 15; //maximum length of array of characters
int ctr = 0;
char * enaString = NULL, xaraktiras, diaxwristiki[LINESIZE];

enaString = (char*)malloc(sizeof(char) * LINESIZE + 1);

if(enaString == NULL)//check if the memory has free blocks
{
printf("error to initillize memory");
exit(1);
}
printf("Eisagete xaraktira mikous %d :\n", LINESIZE);
do{
xaraktiras = getc(stdin);
enaString[ctr] = xaraktiras;
ctr++;
if (ctr == LINESIZE -1)
{
break;
}

}while(xaraktiras != '\n' );

enaString[ctr] = '\0'; //is this considered undefined behaviour?
enaString = NULL;
free(enaString);

printf("eisagete mia leksi diaxwrismou :\n");//ask the user for another word.fails cause it keeps the getc() value from before
scanf(" %15s",diaxwristiki);
diaxwristiki[LINESIZE-1] = '\0';//is this undefined behaviour?

printf("timi diaxwrismou %s\n", diaxwristiki);




}

Answer

To compile the information hidden in the comments below the OP's post and my own 2 cents:

#include <stdio.h>
#include <stdlib.h>
// you don't use anything from string.h in your version
//#include <string.h>

// Put simple constants here, for the preprocessor to process
#define LINESIZE 15

// you don't use the arguments, no need to put them here
int main( /*int argc, char *argv[] */ )
{
  // such constants are better put into a preprocessor directive
  //int LINESIZE = 15; //maximum length of array of characters
  int ctr = 0;

  // Sorted into three lines (three different types) better to read

  // No need to initialize enaString to NULL for malloc/calloc
  // It is a good idea to do for realloc(), safes you
  // the initial malloc() but you do not use realloc() here
  char *enaString;
  // (f)getc() and scanf() return an int
  int xaraktiras, ret_scanf;
  char diaxwristiki[LINESIZE];

  // no casting of malloc() in C
  enaString = /*(char*) */ malloc(sizeof(char) * LINESIZE + 1);
  if (enaString == NULL)        //check if the memory has free blocks
  {                             
    // use stderr stream for error output
    // (sderr might not be available but worth a try)
    // UX-tip: use the same language for errors that you
    // use for user interaction elsewhere
    fprintf(stderr, "error to initillize memory");
    // Use the macros from stdlib.h, the return values
    // are OS dependent and might not be 0 and 1 respectively
    exit(EXIT_FAILURE);
  }
  // you ask for a word of a certain size or only for a word?
  // (My Greek is not very good and Google is of not much help here)
  printf("Eisagete xaraktira mikous %d :\n", LINESIZE);
  do {
    // please be aware the getc() is in most cases implemented
    // as a macro, use fgetc() if you are not sure if that is
    // a problem (it is not here) because macros might get evaluated
    // more than once
    xaraktiras = getc(stdin);
    // you need to check for EOF somewhere. Here would be a good place
    if (xaraktiras == EOF) {
      // try it by pressing CTRL+D instead of feeding characters to getc()
      fprintf(stderr, "EOF found in getc() loop\n");
      // EOF might also indicate an error, see the handling of scanf() below
      // We don't bother with it now, we just exit
      exit(EXIT_FAILURE);
    }
    // no need for a cast here
    enaString[ctr] = xaraktiras;
    // put it after the check, otherwise you have an undefined
    // character at enaString[ctr]
    // ctr++; 
    if (ctr == LINESIZE - 1) {
      // You offered LINESIZE, have allocated LINESIZE+1, but only
      // allow LINESIZE-1
      // The user might be disappointed
      break;
    }
    ctr++;
    // No casting needed, because the type of a char constant is int
    // (yes, that means that things like "char c='STOP'" once worked and you
    //  were able to look for 0x53544f50 in the memory dump. Some compilers might
    //  still allow for it but it is not recommended)
  } while (xaraktiras != '\n');

  // slurp the rest up if there were more characters given
  // (check for EOF ommitted here but should be added, of course)
  if(xaraktiras != '\n'){
     while ((xaraktiras = getc(stdin)) != '\n');
  }

  // you go up to LINESIZE-1 now, so, together with the replacement of ctr++, it is OK
  enaString[ctr + 1] = '\0';    //is this considered undefined behavior?

  // don't just dump the painfully gathered characters, print them at least.
  // That way you'll find out that you included the '\n', too, which
  // might or might not have been your intent
  printf("enaString = \"%s\"\n",enaString);

  // To free the memory free() needs to know where it is and
  // the pointer enaString points to that memory. If you set
  // enaString to NULL free() does not know which memory to free
  // (worse: free(NULL) is allowed) and the memory
  // is left alone, crying, and is unreachable until the program ends,
  // a so called "memory leak"

  // enaString = NULL;
  // free(enaString);
  free(enaString);
  // I don't know who told you so, but it is indeed a good idea to set
  // the pointer to the free'd memory to NULL. Won't do anything here
  // but might safe you from a lot of headaches in large programs
  enaString = NULL;

  printf("eisagete mia leksi diaxwrismou :\n"); //ask the user for another word.

  // the variable "diaxwristiki" can hold 15 characters , "%15s" allows for 16, because
  // scanf() includes `\0` (EOS, NUL, nul, or whatever the kids call it today), too!

  // scanf() returns the number of elements (not characters!) read or EOF.

// for strerror()
#include <string.h>
// for errno
#include <errno.h>
  // reset errno, just in case
  errno = 0;
  if ((ret_scanf = scanf(" %14s", diaxwristiki)) == EOF) {
    // It also returns EOF in case of an error, so check for it
    if (errno != 0) {
      fprintf(stderr, "error in scanf: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }
    // try it by pressing CTRL+D instead of feeding characters to scanf()
    fprintf(stderr, "EOF triggered by scanf()\n");
    // diaxwristiki might contain rubbish at this point, clear it
    diaxwristiki[0] = '\0';
  }
  // no need for adding EOS, scanf() already added it
  // diaxwristiki[LINESIZE-1] = '\0';//is this undefined behaviour?
  printf("timi diaxwrismou %s\n", diaxwristiki);
  // it's "int main()", so return something.
  exit(EXIT_SUCCESS);
}