LABORATORY 03

Console Input/Output Operations

Duration: 2 hours Library: stdio.h Previous: Laboratory 2 PDF handout RO versiunea română

In C, input/output operations are not part of the language: there are no dedicated statements, only functions added through the standard library. This separation gives flexibility, but shifts onto the programmer the responsibility of correctly specifying the format of the data.

1Lab objectives

  • Understanding the concept of a stream and the five predefined logical devices
  • Using character-level read/write functions
  • Reading and printing character strings
  • Correctly using format specifiers in printf and scanf
  • Recognizing and avoiding the classic pitfalls of the scanf function

2Standard streams

keyboardcharacter streamcininput buffervariableknown typekeystrokesthe >> operatorscreendisplayed textcoutoutput buffervariablethe << operatorflushing the buffer
Fig. - The two standard streams. Data does not go directly from the keyboard to the variable: a buffer sits between them, which explains why unread leftover characters affect the next read.

Transfers to and from physical devices happen through logical devices called streams. There are two categories: text streams, which transfer characters organized into lines, and binary streams, which transfer bytes with no imposed structure.

StreamTypeAssociated withCan be redirected?
stdintextthe keyboard - standard inputyes
stdouttextthe monitor - standard outputyes
stderrtextthe monitor - the error outputyes
stdprnbinarythe printer (DOS PRN device)no
stdauxbinaryauxiliary input/output (DOS AUX)no
Why stderr exists separately from stdoutError messages travel on a separate stream, so that when redirecting output to a file (program > result.txt) errors still show up on the screen, instead of being hidden in the file.

3Character-level operations

FunctionRoleNotes
int getchar(void)reads a character from stdinwaits for Enter; returns EOF at end of file
int getch(void)reads a character with no echodoes not wait for Enter; specific to conio.h, non-portable
int getche(void)reads a character with echo on screendoes not wait for Enter; non-portable
int putchar(int c)writes a character to stdoutreturns the character written
Why it returns int, not chargetchar() must be able to signal the end of the stream through the value EOF (usually −1), which does not fit in the unsigned char range. If the result is stored in a char, the test c != EOF may never work.

4String operations

FunctionRoleRisk
gets(s)reads an entire line, including spacesdangerous - does not limit the length; removed from the standard
fgets(s, n, stdin)reads at most n−1 characterssafe; keeps the trailing '\n' character
puts(s)prints a string and adds a newline-
scanf("%s", s)reads a word (stops at whitespace)cannot read sentences with spaces
Practical ruleAlways use fgets instead of gets. To remove the leftover newline character: s[strcspn(s, "\n")] = '\0';

5Formatted input/output

The structure of a specifier

general form
%[flag][width][.precision][modifier]type

   %-8.3f
   │││ │ └── type: f = real
   ││└─┴──── precision: 3 decimals
   │└─────── minimum width: 8 characters
   └──────── flag: - = left alignment
SpecifierData typeExample output
%d, %iint-42
%uunsigned int4294967254
%ffloat / double3.141593
%.2freal with 2 decimals3.14
%escientific notation3.141593e+00
%ccharA
%scharacter stringtext
%x, %Xhexadecimalff / FF
%ooctal377
%paddress (pointer)0x7ffd3a2c
%%a literal percent character%

6Specifier explorer

Choose a specifier and a value, then watch exactly what printf prints. Try mismatched combinations - for example %c with the value 65 - to understand why the compiler does not warn you.

printf - specifier, value, result

7Reading pitfalls

1. Missing the & operator in scanf scanf("%d", n); instead of scanf("%d", &n); passes the value of the variable instead of its address. The program compiles, but crashes badly at run time.
2. The Enter character left in the buffer After scanf("%d", &n);, the Enter key is left unread in stdin. An immediately following scanf("%c", &c); will pick up that '\n', not the intended character. Solution: scanf(" %c", &c); - the space in the format skips over whitespace characters.
3. Mismatched specifier printf("%d", 3.14); produces no compile error, but prints a meaningless value, because it interprets the bits of a double as an int.
4. Invalid input data If the user types letters when a number is expected, scanf returns 0 and leaves the text in the buffer, which can lead to an infinite loop. Always check the return value: if (scanf("%d", &n) != 1) { ... }

8Source code

characters.c - counting characters read
#include <stdio.h>

int main(void)
{
    int c;                  // int, NOT char, so it can be compared with EOF
    int letters = 0, digits = 0, spaces = 0, other = 0;

    printf("Enter text (Ctrl+Z / Ctrl+D to finish):\n");

    while ((c = getchar()) != EOF) {
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) letters++;
        else if (c >= '0' && c <= '9')                        digits++;
        else if (c == ' ' || c == '\t' || c == '\n')          spaces++;
        else                                                  other++;
    }

    printf("\nLetters: %d  Digits: %d  Spaces: %d  Other: %d\n",
           letters, digits, spaces, other);
    return 0;
}
safe_read.c - robust reading
#include <stdio.h>
#include <string.h>

int main(void)
{
    char name[50];
    int age;

    printf("Full name: ");
    fgets(name, sizeof(name), stdin);           // safe: limits the length
    name[strcspn(name, "\n")] = '\0';           // remove the newline

    printf("Age: ");
    while (scanf("%d", &age) != 1) {            // check that reading succeeded
        printf("Invalid value. Try again: ");
        while (getchar() != '\n');              // flush the buffer
    }

    printf("\n%-20s %3d years old\n", name, age);
    return 0;
}
formatting.c - an aligned table
#include <stdio.h>

int main(void)
{
    printf("%-12s %8s %10s\n", "Product", "Qty", "Price");
    printf("--------------------------------\n");
    printf("%-12s %8d %10.2f\n", "Resistor",   120,  0.15);
    printf("%-12s %8d %10.2f\n", "Capacitor",  45,  0.80);
    printf("%-12s %8d %10.2f\n", "Microcontroller", 8, 24.50);

    // the same number, in different bases
    int v = 255;
    printf("\n%d in decimal = %o in octal = %X in hex\n", v, v, v);

    // printing a literal percent sign
    printf("Progress: %d%%\n", 75);

    return 0;
}

9Code workshop

Output formatting is learned by experimenting. Change the numbers inside the specifiers (%8.3f, %-12s) and watch the columns move.

Format specifiers, seen at work
#include <stdio.h>

int main(void)
{
    int    n = 42;
    double x = 3.14159265;
    char   c = 'Q';
    char   s[] = "text";

    printf("[%d]      plain integer\n", n);
    printf("[%8d]  width 8, right aligned\n", n);
    printf("[%-8d]  width 8, left aligned\n", n);
    printf("[%08d]  zero padded\n", n);

    printf("\n[%f]      default: 6 decimals\n", x);
    printf("[%.2f]        two decimals\n", x);
    printf("[%10.3f]  width 10, three decimals\n", x);

    printf("\n[%c] [%s] [%x in hex] [%o in octal]\n", c, s, n, n);
    return 0;
}
Reading from the keyboard - exercise
#include <stdio.h>

int main(void)
{
    char   name[30];
    int    age;
    double average;

    printf("Name: ");
    scanf("%s", name);

    /* Also read the age (integer) and the average (double).
       Careful: with scanf, numeric variables need & */

    printf("\n%-12s %3d years old   average %.2f\n", name, age, average);
    return 0;
}
Try thisThe input data field above stands in for the keyboard: each value there is picked up by one scanf, in order. In a real program, the user would type them one at a time.

10Work tasks

  • Write a program that reads characters until EOF and counts the letters, digits, and spaces.
  • Read a full name (with spaces) using fgets and print it with puts.
  • Reproduce the leftover-Enter-in-the-buffer trap, then fix it with " %c".
  • Print the number 255 in decimal, octal, and hexadecimal, in a single printf.
  • Build an aligned table with three columns, using explicit widths and alignments.
  • Implement robust reading of an integer, with retry on invalid data.
  • Test what printf("%d", 3.14) prints and explain the result.

11Extended application

ExtensionWrite a small "text analyzer": the program reads a sentence and prints the number of words, the longest word, the number of vowels and consonants, plus the frequency of each letter as a histogram of asterisks. Use only functions from stdio.h and ctype.h.

12Review questions

13Resources