CS140 Lecture notes -- Types, Pointers

Jim Plank with some modificatins by Brad Vander Zanden

argv/argc, sscanf(), atoi(), shell indirection, stderr

If you have not learned about argc/argc, here is the information. If you declare your main() routine as follows:
int main(int argc, char **argv)
Then you can access the command line arguments that are given when the program starts. The number of arguments is in argc, and the arguments themselves are in argv. There is always at least one command line argument: the program name itself. Here's a simple program, pcli.c, that prints out the command line arguments:

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

main(int argc, char **argv)
{
  int i;

  for (i = 0; i < argc; i++) {
    printf("Argument %2d: %s\n", i, argv[i]);
  }
  return 0;
}

UNIX> pcli
Argument  0: pcli
UNIX> pcli Jim Plank
Argument  0: pcli
Argument  1: Jim
Argument  2: Plank
UNIX> pcli Give Him Six
Argument  0: pcli
Argument  1: Give
Argument  2: Him
Argument  3: Six
UNIX> 
sscanf() is an important procedure that allows you to perform type conversion from strings. It works just like scanf(), except you give it a string as the first argument, and it reads from that string rather than standard input. Like scanf(), I recommend that you only perform one conversion with each call to sscanf(). It returns the number of successful conversions that it made. Here is a program , ssc.c, that prints out each command line argument in four ways: as a string, converted to an integer, converted to a double, and converted to hexadecimal. If the conversion is unsuccessful, it makes note of it:

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

main(int argc, char **argv)
{
  int i;
  int j;
  double f;

  for (i = 0; i < argc; i++) {
    printf("Argument %2d: %-20s", i, argv[i]);

    if (sscanf(argv[i], "%d", &j) == 1) {  /* Convert to an int and print */
      printf(" %10d", j);
    } else {
      printf(" %10s", "-");
    }

    if (sscanf(argv[i], "%lf", &f) == 1) {  /* Convert to a double and print */
      printf(" %10.3lf", f);
    } else {
      printf(" %10s", "-");
    }

    if (sscanf(argv[i], "%x", &j) == 1) {  /* Convert to hex and print */
      printf(" 0x%08x", j);
    } else {
      printf(" %10s", "-");
    }

    printf("\n");
  }
  return 0;
}

Make sure you go over the program and this output until you understand it. remember that 0x66 does not equal 66. Also note how it converts argument number 4 in hex -- the "F" is part of the conversion.

UNIX> ssc J 5 66.88 29Fred ff
Argument  0: ssc                           -          -          -
Argument  1: J                             -          -          -
Argument  2: 5                             5      5.000 0x00000005
Argument  3: 66.88                        66     66.880 0x00000066
Argument  4: 29Fred                       29     29.000 0x0000029f
Argument  5: ff                            -          - 0x000000ff
UNIX> 
atoi() is a procedure that converts a string to an integer. It is kind of like sccanf(), except it returns zero when it can't convert the string. Thus, error-checking atoi() is difficult if zero is a legal input, since you cannot differentiate zero from a bad string. Still, sometimes it is useful. However, I am going to caution you from using it (or atof()), as it will lead to bugs down the line.

As you have probably seen in earlier classes, when you execute programs in your command-line interpreter, called the "shell", you can have input and output be redirected from and to files. The less-than sign has input come from a file. For example, the grep command prints all lines from standard input that contain the word given on its command-line argument. So, we can grep for the word main in the program ssc.c, and it will print out the program's main() line:

UNIX> grep main < ssc.c
main(int argc, char **argv)
We can have standard output redirected to a file with the greater-than sign:
UNIX> grep main < ssc.c > output.txt
UNIX> cat output.txt
main(int argc, char **argv)
If you didn't know about the program cat, it simply prints out all of the files on its command line to standard output. If there are no command line arguments, it prints standard input.
UNIX> grep for ssc.c
  for (i = 0; i < argc; i++) {
UNIX> grep for ssc.c > output2.txt
UNIX> cat output.txt output2.txt
main(int argc, char **argv)
  for (i = 0; i < argc; i++) {
UNIX> cat < output2.txt
  for (i = 0; i < argc; i++) {
UNIX> 
The vertical bar is a "pipe". It has the standard output of one command go to standard input of the next:
UNIX> ls -l | grep output
-rw-r--r--  1 plank loci    28 Aug 27 12:10 output.txt
-rw-r--r--  1 plank loci    31 Aug 27 12:13 output2.txt
UNIX> ls -l | grep output | grep t2
-rw-r--r--  1 plank loci    31 Aug 27 12:13 output2.txt
UNIX> ls -l | grep output | grep t2 | cat
-rw-r--r--  1 plank loci    31 Aug 27 12:13 output2.txt
UNIX> 
Finally, there are two sets of output to every program -- standard output and standard error. Typically, normal program output should go to standard output, and errors should go to standard error. Regular printf() statements go to standard output. To print to standard error, do fprintf(stderr, ...), and have the rest of the statement look like a printf() statement.

For example, the following program, stde.c, averages all numbers on the command line. However, if any argument is not a number, it prints an error to standard error:

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

main(int argc, char **argv)
{
  int i;
  double total, n, x;

  total = 0;
  n = 0;

  if (argc == 1) {
    fprintf(stderr, "usage: stde numbers...\n");
    exit(1);
  }

  for (i = 1; i < argc; i++) {
    if (sscanf(argv[i], "%lf", &x) == 1) {
      total += x;
      n++;
    } else {
     fprintf(stderr, "Argument %s not a number\n", argv[i]);
     exit(1);
    }
  }
  printf("%.3lf\n", total/n);
  return 0;
}

Note how when we redirect standard output to a file, standard error still gets printed to the screen.

UNIX> stde
usage: stde numbers...
UNIX> stde > output3.txt
usage: stde numbers...
UNIX> cat output3.txt
UNIX> stde 23 43 23 64 > output3.txt
UNIX> cat output3.txt
38.250
UNIX> stde 23 43 23 64 Fred > output3.txt
Argument Fred not a number
UNIX> cat output3.txt
UNIX>