Command line arguments: argc and argv

Until now, we have been writing our main function as:
int main () {
...
It turns out that main can also accept some parameters. The first parameter is an integer, usually called argc, that tells how many command line arguments there are, including the command itself. The command line arguments describe what the Unix command line looked like when the program was executed. The second argument to main, usually called argv, is an array of strings. A string is just an array of characters, so argv is an array of arrays. There are standard C functions that manipulate strings, so it's not too important to understand the details of argv, as long as you see a few examples of using it.

For example, suppose you want to just look at the command line arguments to a program. The printf %s token prints strings, just like %i prints integers:
#include <stdio.h>
#include <stdlib.h>

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

	for (i=0; i<argc; i++) printf ("%s\n", argv[i]);
	exit (0);
}
Let's call this program foo, so we would compile it with
cc -o foo foo.c
If the user simply executes it like this:
./foo
then the output would be
foo
argc would be 1, and argv[0] would be the string "foo". If the user types
./foo 1 2 3 hello
then the output would be
foo
1
2
3
hello
argc would be 5.

Suppose we want to write a program that accepts two command line arguments and prints their sums:

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

int main (int argc, char *argv[]) {
	int	a, b;

	if (argc != 3) {
		fprintf (stderr, "wrong number of arguments!\n");
		exit (1);
	}
	a = atoi (argv[1]);
	b = atoi (argv[2]);
	printf ("%i\n", a + b);
	exit (0);
}
The two calls to atoi convert the strings argv[1] and argv[2] to integers. atoi stands for "ASCII to integer."

It's important to remember that argc is always one more than the number of command line arguments, since it also includes the name of the program in argv[0]. So if your program expects only one argument, it should make sure argc is equal to 2, then look for that argument in argv[1].

More on Arrays

Let's look at some more programs using arrays.

Suppose we have a file full of student grades, and we want to see which grades are above average. We can read the grades into an array, find the average, then go through the array printing out each grade that is above average:
#include <stdio.h>
#include <stdlib.h>

#define MAX_STUDENTS	100

int main () {
	int	nstudents, i;
	float	grades[MAX_STUDENTS], sum, avg;

	nstudents = 0;

	/* loop, filling the array with values from scanf */

	for (;;) {
		scanf ("%f", &grades[nstudents]);
		if (feof (stdin)) break;
		nstudents++;

		/* too many?  complain and exit */

		if (nstudents > MAX_STUDENTS) {
			fprintf (stderr, "too many students!\n");
			exit (1);
		}
	}

	/* nothing on stdin?  complain and exit. */

	if (nstudents == 0) {
		fprintf (stderr, "not enough students!\n");
		exit (1);
	}

	/* find average of grades in array */

	sum = 0.0;
	for (i=0; i<nstudents; i++) sum += grades[i];
	avg = sum / nstudents;

	/* go through printing grades that are above average */

	for (i=0; i<nstudents; i++)
		if (grades[i] > avg) printf ("%f\n", grades[i]);
	exit (0);
}

Functions and Arrays

Functions can accept array parameters. Usually the size of the array must be specified to the function, since there's no way to tell the size of an array once it has been passed as a parameter. The following function will return the average of the first n floats in the array v:
float find_average (float v[], int n) {
	int	i;
	float	sum;

	for (i=0,sum=0.0; i<n; i++) sum += v[i];
	return sum / n;
}
Because of some weird things going on behind the scenes (trust me, you don't want to know), arrays passed to functions are not passed by value; if an array element is changed in the function, it remains changed in the original array. This makes things more efficient since a new copy of the array doesn't have to be made, but you have to be careful not to change the contents of an array in a function unless you really mean it. Usually this isn't a problem; in fact, it often comes in handy. The following function will initialize the first n elements of an integer array to 0:
void initialize_to_zero (int w[], int n) {
	int	i;

	for (i=0; i<n; i++) w[i] = 0;
}
This way, you can initialize lots of different arrays without having to write lots of different for loops.