Guides:C/C Crash Course/Standard Input (stdin)

From CoderGuide

Jump to: navigation, search

Back to TOC

Contents

Standard input (stdin)

scanf(char *format, ...)

scanf() is used to read data in from stdin, and also takes a similar format to printf(). The catch here is that all parameters passed to scanf() must be pointers. This allows scanf() to alter the data in the parameters passed it. How can a function do this? Well, here's a quick example.

#include <stdio.h>
 
void a_func(int); /*we must have a function prototype */
void b_func(int *);
 
int main(){
        int i;
        i=10;
        printf("%d ",i);
        b_func(&i);
        printf("%d ",i);
        a_func(&i);
        printf("%d\n",i);
}
 
b_func(int i){
        i=100;
}
 
a_func(int *i){
        *i=12345;
}

If you compiled and ran this program, the output would be:

10 10 12345

Remember, to convert a non-pointer variable into a pointer, you prefix the name with a ampersand &. Arrays (including strings) are treated as pointers if you're referring to the entire array, and not a single element of it (given array ar, ar would refer to the entire array while ar[2] would just refer to one element of it).

scanf() also takes a format string, but it's used, not for printing formatted text to the screen, but describe how data should be read in. Here is a partial list of flags:

CodeFunction
%sread a string up to the first whitespace
%dread an integer
%lread a long integer
%fread a float
%ureads an unsigned integer

And a quick example:

#include <stdio.h>
 
int main(){
        int age;
        char name[40];
 
        printf("What is your name?");
        scanf("%s",name);
        printf("What is your age?");
        scanf("%d",&age); /*We must pass a reference to scanf
                                for non-pointers*/
        printf("\nHello %s!",name);
        if (age>=50)
                printf("Wow, you're an antique!");
        else if (age<=16)
                printf("Hey there genius!");
        printf(" See ya!\n");
        printf("\n\nRead age as:%d read name as:\"%s\"\n",age,name);
}

If you were to run this program, then you'd get the following output (Text that you type in is in bold):

What is your name?Douglas
What is your age?26

Hello Douglas! See ya!


Read age as:26 read name as:"Douglas"

Cool eh? Well, remember that little note about buffered I/O, and, if you don't read all characters in from the buffer, there's still data left in it? Well, here's a good example of problems that can cause if we ran our program with different inputs:

What is your name?Johnny Doe
What is your age?
Hello Johnny!Hey there genius! See ya!


Read age as:0 read name as:"Johnny"

What happened? Well, scanf() stopped reading character after the first white space, and so "Doe" was left in the buffer when it came time to read the age in. Since "Doe" isn't a number, scanf() just barfed and left a zero in age.

It gets worse: the input stream is now corrupted because scanf() didn't get what it was expecting, and now, whenever you try reading input, scanf() will still be throwing a temper-tantrum, and you won't be able to read any more data in until it's error status of that stream is cleared, which is more trouble checking and fixing that it's worth. This is a problem with pretty much all C and C++ compilers that I've used (including, when using C++ std::cin interface), hence I never use scanf() in any program I write (and only use std::cin for reading a single character or string in C++).

So what is the solution, well, use different functions (and write your own utility functions)!

char *gets(char *s)

gets() reads a string of text in from the terminal, and puts it into s. It will continue reading until a newline is encountered (as generated by the return or enter key), or until the end of the input stream is reached (EOF end of file/input).

gets(), like scanf() performs no bounds checking, that means if you type more characters than your character array can hold, it will begin overwriting other data in your program, so make sure your array is big enough to hold the data you want to read (or use a safer function). In fact, GCC will warn you that you should use a different function when you compile your code using it.

#include <stdio.h>
 
int main(){
        char name[255];
 
        printf("What is your name?");
        gets(name);
        printf("\nHello %s!\n",name);
}
And it's output:
What is your name?Johnny Doe
Hello Johnny Doe!

Some of the example code in this crash course will use the gets() function even though it is unsafe. This is only for simplicity's sake; however, if you make a your own program for real use, you should use one of the other functions below, or write your own safer function.

char *fgets(char *s, int size, FILE *stream)

This function was designed for reading from any stream, including files. It works similar to gets(), except it will not read any more characters than size characters, leaving the rest in the buffer, and it also reads in the newline character. This means that fgets() is safer to use than gets() when reading input.

FILE isn't a data type, it is actually a defined type that contains information about a stream. You can make your own streams when you open a file, however, a few streams already exist when you start your program. They are stdin, stdout, and stderr. We've already talked about stdin and stdout. stderr is another output stream, but is is used for printing errors to the screen. On Unix systems, text sent stderr will still display to the screen, even if the output from stdout is being redirected to a file or another program.

Okay, now for a little example:

#include <stdio.h>
 
int main(){
        char name[50];
 
        printf("What is your name?");
        fgets(name,50,stdin);
        printf("\nHello %s!\n",name);
}
And it's output:
What is your name?Johnny Doe

Hello Johnny Doe
!

Wait a second, what did the explanation point appear on a new line? That is because fgets() also reads in the newline character, gets strips the newline character off the end. If you don't want to have the newline character at the end, you'll have to remove it as a separate step.

int getchar()

Reads a character from stdin, pretty simple.

#include <stdio.h>
 
/***** 
  sgets(char *s, int size)
 
  A safer gets.
 
  s is the string to be read
  size is the size of the string
 
*****/
char *sgets(char *,int);
 
int main(){
        char name[30];
        printf("What is your name?");
        sgets(name,30);
        printf("Hello %s!\n",name);
}
 
char *sgets(char *s, int size){
        int i;
        char ch;
        ch=getchar();
 
        /*We have size=size-1, because we need space to add the
          null terminator. */
        size--;
        for(i=0;ch!='\n' && i<size;i++){
 
                /*make sure we haven't reached the end of
                  the input stream*/
 
                if(feof(stdin))break;
 
                s[i]=ch;
                ch=getchar();
        }
        s[i]=0; /*add the null terminator*/
 
        /* If we stopped short of reading up to the end of the
           input line, clear the extra data in the buffer */
 
        if(ch!='\n' && !feof(stdin)){
 
                for(;ch!='\n' && !feof(stdin); ch=getchar());
        }
 
        return s;
} /*end of sgets()*/

Now your program output will be the same as it would be for gets() and it will be safe to use. Guess what, you just wrote yourself a safe version of gets()! Now you can add this code to your own personal library of functions to use, and reuse over and over again.

Personal tools