Name of printf and scanf

Print function in C abbreviated to printf() and scan function abbreviated to scanf. Printf and scanf are most frequently used in any programs written with C language. These functions implicitly work on console stdin/stdout files. There are other variations of these functions. fprintf/fscanf operates on file buffer and sscanf/sprintf can operates on string buffer. These functions are adapted to work on different I/O buffer but they all work on the same principle.

Variadic functions

Printf and scanf functions are based on Variadic functions. Variadic functions can take a variable number of arguments. To understand printf or scanf we need to first understand how Variadic functions work. Variadic functions should have at least one argument and after that "..." should be added to indicate that this function takes variable arguments. Header file contains type va_list and other variable argument utility macros to write these functions.

Variadic types & macros

Type Defined in header file <stdarg.h>

va_list (type) 
holds the information needed by macros va_start, va_arg, va_end, and va_copy

Macros Defined in header file <stdarg.h>

va_start (macro)
start of iterations in the variadic function arguments

va_arg (macro)
get the next variadic function argument

va_copy (macro)
makes a copy of the variadic function arguments
(Available only in C99)
 
va_end (macro)
ends of iteration in the variadic function arguments

Variadic function example

#include <stdio.h>
#include <stdarg.h>
 
void variadic_funct(int count, ...)
{
    va_list args;
    int i;
    va_start(args, count);
    printf ("variadic : argument count = %d\n", count);
    for (= 0; i < count; i++) {
     printf("argument %d = %d, ", i + 1, va_arg(args, int));
    }
    printf ("\n");
    va_end(args);
}
 
int main(void)
{
    variadic_funct(0); 
    variadic_funct(1,10);
    variadic_funct(2,10,20);
    variadic_funct(3,10,20,30);  
}

Variadic functions output

./a.out
variadic : argument count = 0

variadic : argument count = 1
argument 1 = 10, 
variadic : argument count = 2
argument 1 = 10, argument 2 = 20, 
variadic : argument count = 3
argument 1 = 10, argument 2 = 20, argument 3 = 30,

Printf working principle

printf or print function in C takes a formatting string and couple of optional variables as input and outputs strings to console while converting input variables to strings.

Printf block diagram

Printf and scanf takes multiple arguments and these functions are called variable length arguments function or vararg function. Take printf for consideration. User supply a string and input argumnets. Printf creates an internal buffer for constructing output string. Now printf iterates through each characters of user string and copies the character to the output string. Printf only stops at "%". "%" means there is an argument to convert. Arguments are in the form of char, int, long, float, double or string. It converts it to string and appends to output buffer. If the argument is string then it does a string copy. Finally printf may reach at the end of user sting and it copies the entire buffer to the stdout file.

Implement printf

Let us implement our own printf function. This is only for the understanding purpose. We name it print(). It has one string argument (str) and rest are variable arguments. Variable arguments are managed by macros like va_start, va_arg and va_end. A temporary buffer (buff) is there to construct the output buffer. A while loop is needed to scan each characters in the input string. Now we iterate character by character in the loop and copy each character to output string. Same time we check for "%". "%" is not copied to output string. Once we found it, we check the next character. This is the formatting character. Formatting character says how to format the argument to visible output string. Printf supports varieties of formatting. C is for character, d for decimal integer, f for floating point, x for hexadecimal and s for strings. We match the formatting and pick the argument variable using va_arg(). Argument variable is then converted to string format and appends to the output string. character can be copied as it is and Itoa function is used for integer to string conversion.

C itoa() function has been used to convert argument integer to string. Integer to String conversion is a process to take each digits and convert those to ASCII format. Visit our topic integer to string conversionfor further understanding.

This process of coping characters and conversion of arguments repeats until the string is terminated to last NULL character. For simplicity we have implemented only c, d, x formatting cases. Now at the end we have the output string ready. This is now passed to fwrite() to stdout. Thus the output string prints in the actual console. Printf then returns the number of characters which is printed in the console and exit the function.

/* Note: this is a minimal printf implementation */
/* This is for building understanding only */

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

char *_strrev (char *str)
{
  int i;
  int len = 0;
  char c;
  if (!str)
    return NULL;
  while(str[len] != '\0'){
    len++;
  }
  for(= 0; i < (len/2); i++)
  {
    c = str[i];
    str [i] = str[len - i - 1];
    str[len - i - 1] = c;
  }
  return str;
}

char * _itoa(int i, char *strout, int base)
{
  char *str = strout;
  int digit, sign = 0;
  if (< 0) {
    sign = 1;
    i *= -1;
  }
  while(i) {
    digit = i % base;
    *str = (digit > 9) ? ('A' + digit - 10) : '0' + digit;
    i = i / base;
    str ++;
  }
  if(sign) {
  *str++ = '-';
  }
  *str = '\0';
  _strrev(strout);
  return strout;
}

int print (char * str, ...)
{
  va_list vl;
  int i = 0, j=0;
  char buff[100]={0}, tmp[20];
  char * str_arg;
  
  va_start( vl, str );
  while (str && str[i])
  {
    if(str[i] == '%'){
      i++;
      switch (str[i]) {
        /* Convert char */
        case 'c': {
          buff[j] = (char)va_arg( vl, int );
          j++;
          break;
        }
        /* Convert decimal */
        case 'd': {
          _itoa(va_arg( vl, int ), tmp, 10);
          strcpy(&buff[j], tmp);
          j += strlen(tmp);
          break;
        }
        /* Convert hex */
        case 'x': {
          _itoa(va_arg( vl, int ), tmp, 16);
          strcpy(&buff[j], tmp);
          j += strlen(tmp);
          break;
        }
        /* Convert octal */
        case 'o': {
          _itoa(va_arg( vl, int ), tmp, 8);
          strcpy(&buff[j], tmp);
          j += strlen(tmp);
          break;
        }
        /* copy string */
        case 's': {
          str_arg = va_arg( vl, char* );
          strcpy(&buff[j], str_arg);
          j += strlen(str_arg);
          break;
        }
      }
    } else {
      buff[j] =str[i];
      j++;
    }
    i++;
  } 
  fwrite(buff, j, 1, stdout); 
  va_end(vl);
  return j;
}
int main (int argc, char *argv[])
{
  int ret;
  ret = print("%c %d %o %x %s\n", 'A', 10, 100, 1000, "Hello from printf!");
  printf("printf returns %d bytes\n", ret);
  return 0;
}

Output

A 10 144 3E8 Hello from printf!
printf return 32

Scanf working principle

scanf or scan function in C takes a formatting string as input and couple of optional variables as output reference arguments. It converts the scanned string to variables and copies to the output variables.

Scanf block diagram

Scanf is reverse process of printf. Scanf reads console input string. It iterates each characters of user provided string and stops at "%". Now scanf reads a line from stdin. User's input comes as a string. It converts string to char, int, long, float, double and sets the value of the pointer located at the argument. In care of string it simply copies the string to the output

Implement scanf

Let us implement our own scan function. This is only for the understanding purpose. We name it scan(). It has one string argument (str) and rest are variable arguments. Variable arguments are managed by macros like va_start, va_arg and va_end. A temporary buffer (buff) is there to copy buffer from stdin. In a while loop we read characters from stdin and and copy to buff. Loop terminates when user presses return key. Now we have another while loop to scan the buffer. We iterate character by character and we check for "%". Once we found it, we check the next character. This is the formatting character. Formatting character says how to argument is formatted in the buffer. Scanf supports varieties of formatting. C is for character, d for decimal integer, f for floating point, x for hexadecimal and s for strings. We pick the argument variable using va_arg(). Argument variable is then converted from string to desired type. character can be copied as it is and strtol() function is used for string to integer conversion.

C strtol() function has been used to convert argument string to integer. String to integer conversion is a process to take each ASCII characters and convert those to integer. Visit our topic string to integer conversionfor further understanding.

This process of taking each character and convert formatted string to integers repeats until the string is terminated to last NULL character. For simplicity we have implemented only c, d, x formatting cases. Now at the end we have the output string ready. Scanf then returns the number of output argument is successfully converted and exits.

/* Note: this is a minimal scanf implementation */
/* This is for building understanding only */
#include <stdio.h>
#include <stdlib.h>
#include<stdarg.h>
int scan (char * str, ...)
{
  va_list vl;
  int i = 0, j=0, ret = 0;
  char buff[100] = {0}, tmp[20], c;
  char *out_loc;
  while(!= '\n') {
    if (fread(&c, 1, 1, stdin)) {
      buff[i] = c;
      i++;
    }
  }
  va_start( vl, str );
  i = 0;
  while (str && str[i]) {
    if (str[i] == '%') {
      i++;
    switch (str[i]) {
        case 'c': {
          * (char *)va_arg( vl, char* ) = buff[j];
          j++;
          ret ++;
          break;
        }
        case 'd': {
          * (int *)va_arg( vl, int* ) = \
          strtol(&buff[j], &out_loc, 10);
          j += out_loc -&buff[j];
          ret++;
          break;
        }
        case 'x': {
          * (int *)va_arg( vl, int* ) = \
          strtol(&buff[j], &out_loc, 16);
          j += out_loc -&buff[j];
          ret++;
          break;
        }
        case 'o': {
          * (int *)va_arg( vl, int* ) = \
          strtol(&buff[j], &out_loc, 8);
          j += out_loc -&buff[j];
          ret++;
          break;
        }
        case 's': {
          out_loc = (char *)va_arg( vl, char* );
          strcpy(out_loc, &buff[j]);
          j += strlen(&buff[j]);
          ret++;
          break;
        }
      }
    } else {
      buff[j] =str[i];
      j++;
    }
    i++;
  }
  va_end(vl);
   return ret;
}
int main(int argc, char *argv[])
{
  char c;
  int i;
  int h;
  int o;
  char str_buff[20];
  int ret = 0;
  printf("Enter char int hex oct string\n");
  ret = scan("%c %d %x %o %s", &c, &i, &h, &o, str_buff);
  printf("C = %c, I = %d, H = %d, O = %d, S = %s returns %d", c, i, h, o, str_buff, ret);
  return 0;
}

Output

Enter char int hex oct string
A 10 100 1000 Hello from scanf!
C = A, I = 10, H = 256, O = 512, S = Hello from scanf!
returns 5

About our authors: Team EQA

You have viewed 1 page out of 248. Your C learning is 0.00% complete. Login to check your learning progress.

#