Example Project

Working with command line arguments

The main() function in a C program most often takes the following form.

int main (int argc, const char * argv[])
{
}

The parameters argc and argv are used to read command line parameters. These notes will explain how the command line mechanism works in C.

The C programming language was developed in conjunction with the UNIX operating system. Originally UNIX was a command-line oriented operating system. Most users of UNIX would get work done by typing text commands at a command line to invoke programs.

One interesting feature of the UNIX command line is that users could pass parameters to programs when invoking them. For example, if we wrote a C program that compiles to a program named 'foo', we could start the program from the command line by typing

./foo

If the program is constructed to accept parameters from the command line we could also start the program and pass it some parameters by typing

./foo 123 bar

When the program starts, it will receive the additional command line parameters in the form of an array of text strings passed to main.

As you can see above, main has two parameters. The first parameter, argc, is an integer count of how many command line parameters the user typed when invoking the program. The second parameter, argv, is an array of text strings containing the actual parameters the user typed. The name of the program itself is always considered the first parameter, and additional parameters are stored as subsequent entries in the array.

For example, if the user invoked our program by typing

./foo 123 bar

at the command line, main would receive an argc parameter with value 3 and an argv parameter containing the three text strings "./foo", "123", and "bar". The program can extract useful information from the array of strings by using the sscanf function to scan in the contents of a string. For example, to convert the parameter "123" to an integer the program would do

int x;
sscanf(argv[1],"%d",&x);

An example

Here is the code for an example program that makes use of a couple of command line parameters.

#include <stdio.h>

int main (int argc, const char * argv[])
{
  int x, N,n;

  // The user will pass us a seed value and a value for N
  // via command line arguments. Since the name of the
  // program is always the first argument, check to see
  // that 3 arguments are present. If we see anything
  // other than 3 arguments, just quit with an error
  // message.
  if(argc != 3)
  {
    printf("Invalid argument list.\n");
    printf("Usage: Random <seed> <N>\n");
    return -1;
  }

  // Read x and N from the arguments
  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&N);

  n = 0;
  while(n < N)
  {
    // Generate the next number in the sequence
    // Mod by 11657 to keep the numbers in a
    // modest range.
    x = (17389*x+12553)%11657;
    n++;
    // Print the number we just generated.
    // Since we want to print numbers in the
    // range from 0 to 999 we have to mod x
    // by 1000 to get the desired value.
    printf("%d\n",x%1000);
  }

  return 0;
}

This program generates a pseudo-random sequence of integers and prints it to the console. The seed, or starting, value for the sequence and the number of numbers to generate are both passed as command line parameters to the program. Near the top of main the program scans those parameters and uses them to set up the logic for what follows.

Compiling and launching programs from the command line

Suppose that the code for the example program above is stored in a c source code file named random.c. To compile this program from the command line we can use the command

gcc random.c -o random

The -o option used here allows us to specify the name of the executable that gets generated when we compile random.c.

Once we have compiled the program we can run it by typing

./random 392 10

This will cause the program to generate 10 random integers starting from a seed value of 392.

Redirection

Another technique that is commonly used with programs on the command line is redirection. In this technique you redirect the input or output stream for a program. For example, you can redirect the standard output for a program to a file.

To run our random number generating program and redirect the output to a file named "random.txt" we can do

./random 102 100 > random.txt

You can also use redirection to force a program to take its input from a file instead of from the keyboard. I will show an example of this below.

Piping commands together

Command line programs that read parameters from the command line and print output to the console are an important part of the Unix ecosystem. A common trick that Unix users will apply with these sorts of programs is piping several short programs together.

One common application of piping is to send the output from one program to another program for further processing. Often, the second program is one of the standard Unix command line utilities, which will perform additional processing on the output of the first program. Here is a typical example. Suppose we wanted to run our random number generator and see only the numbers it outputs that start with the digit 5. We can accomplish this by piping the output of the random program to the Unix grep utility. grep is a utility program that scans lines of input text looking for patterns. In the example below, I tell grep to scan the output generated by the random program looking for lines that start with the digit 5.

./random 120 30 | grep ^5

You can use this technique to chain together two or more of your own programs. The random number generating program I wrote above can serve as a handy source for a stream of random numbers that can be piped to other programs. Below is the source code for a program that does a simple example of a Monte Carlo simulation, which uses a stream of random numbers to simulate a simple physical process. In this case, the process is a simulation of darts being thrown at random at a dart board. The dart board is a circle whose radius R is given by a command line parameter. The program reads N pairs of integers from the keyboard to simulate N darts thrown at a dart board. The dart board sits at the origin and fits in a square of sides 2 R. By counting how many darts land within R of the origin, we can develop a very crude estimate for π.

#include <stdio.h>

int main (int argc, const char * argv[])
{
  int R, N, n, inCircle, x, y;

  // The user will pass us a radius R and a value for N
  // via command line arguments. Since the name of the
  // program is always the first argument, check to see
  // that 3 arguments are present. If we see anything
  // other than 3 arguments, just quit with an error
  // message.
  if(argc != 3)
  {
    printf("Invalid argument list.\n");
    printf("Usage: Darts <R> <N>\n");
    return -1;
  }

  // Read R and N from the arguments
  sscanf(argv[1],"%d",&R);
  sscanf(argv[2],"%d",&N);

  n = 0;
  inCircle = 0;
  while(n < N)
  {
    // Read the coordinates of the next dart
    scanf("%d",&x);
    scanf("%d",&y);
    // Recenter the point to make it fall in the square.
    x = x%(2*R)-R;
    y = y%(2*R)-R;
    // Is the dart within R of the origin?
    if(x*x+y*y <= R*R)
      inCircle++;
    n++;
  }

  // The ratio inCircle/N will produce an estimate for Pi/4.
  // By multiplying by 4.0 we can generate a crude estimate
  // for Pi.
  printf("%f\n",4.0*inCircle/N);

  return 0;
}

If we compile this code to a program named darts, we can pipe the output of the random program to the darts program to get an estimate for π:

./random 120 1000 | ./darts 500 500

We can also use the redirection technique to redirect the output of the Random program to a file and then redirect that file into the input of the Darts program:

./random 120 1000 > random.txt
./darts 500 500 < random.txt

Using a make file

As an alternative to compiling these programs we can use a make system to build the programs. The Linux make utility makes it easy to compile one or more files in a project.

The first step in using make is to create a makefile. A makefile is a text file with the name 'Makefile' that you put in the same directory as your source code files.

Here is the makefile for our project:

all: random darts

random: random.c
  gcc random.c -o random

darts: darts.c
  gcc darts.c -o darts

test: random darts
  ./random 932 1000 > random.txt
  ./darts 500 500 < random.txt

A makefile is a list of one or more targets. Each target has the form

<target name>: <dependencies>
  <build commands>

The <dependencies> are a list of one or more source code files or the names of other targets. The line or lines containing the build commands all have to start with a tab.

For example, the target to build the random program is

random: random.c
  gcc random.c -o random

This says that the target random depends on the C source code file random.c. Whenever the source code file gets modified the make system will automatically recompile the random program when we run the make command.

Once we have set up the makefile we can rebuild all the programs in our project by simply typing the command

make

in the terminal. When you run make in this way it looks for a file named Makefile in the currect directory and tries to build the first target listed there. In this example the first target is the all target, which in turn depends on the random and darts targets. If either of those files needs to be recompiled make will run the commands listed to rebuild them.

The example make file also contains a test target that will recompile the programs if any changes have been made and then run a couple of simple tests to test the two programs. To execute this target we run the command

make test

in the terminal.