Command-Line Arguments

Introduction

Command-line arguments are a great way to give a program input right as the program begins executing. The convention to do this is to give your main function two arguments, argc and argv. argc is an integer related to the number of arguments passed in to the program, and argv is an array of strings related to those arguments, and thus is declared as char* argv[].

Textbook Reading

The necessary background details on processing command-line arguments in C first comes from the textbook. Read

String Processing

Because arguments are given as strings, we introduce or review several important string-processing mechanisms in C.

All examples that follow may be found in the C-program, string-processing.c

Characters and Substrings

Extraction of a character

In C, a string is an array of characters, and each character can be accessed directly through in index within the array.

char myArray[17] = "computer science";
int length = strlen (myArray);
for (int i = 0; i < length; i++)
  printf ("\tmyArray[%d] = %c\n", i, myArray[i]);

Extraction of a substring

Given a string, a common task is to extract a substring from a string. For example, in Scheme, one might use the substring function:

(substring str start end)

According to the Scheme R5 documentation, Substring returns a newly allocated string formed from the characters of string beginning with index start (inclusive) and ending with index end (exclusive).

In C, there is not a function with the name substring, but the function strncpy accomplishes the same result; one copies end - start characters from str, starting with the offset start. If the extracted string does not go to the end of str, then one must remember to add a null character at the end of the extracted string.

The function header of strncpy reads:

char * strncpy(char * dest, const char * src, int n);

In addition to copying the specified number of characters from src to dest, the function returns a pointer to dest.

Several examples follow. Note that string functions are often written to recover gracefully from possible errors, such as in the line

strncpy (subArray, myArray+3, strlen(myArray)); 

in which strncpy will pad subArray with three extra '\0' characters rather than let the extra 3 spaces be filled with undefined characters.

/* extracting substrings from a string */
/* the example uses myArray from above as the starting point */
printf ("extracting substrings from a string\n");
char subArray[20];  /* for a string copy, remember to have allocated space! */
strncpy (subArray, myArray, 8);
subArray[8] = 0; /* null terminate the array */
printf ("\t extract the first 8 characters (plus the null): %s\n", subArray);

/* extra bytes past end of myArray pads subArray with nulls*/
strncpy (subArray, myArray+3, strlen(myArray)); 
printf ("\t extract all characters after the first 3: %s\n", subArray);
  
int start = strlen(myArray) - 8;
strncpy (subArray, myArray+start, 8);
subArray[strlen(myArray)-8] = 0; /* null terminate array */
printf ("\t extract the last 8 characters (plus the null): %s\n", subArray);

Breaking a string into pieces

Some applications require a string to be separated into pieces. For example, consider the string

"January,February,March,April,May,June,July,August,September,October,November,December" 

We might want to break it into a sequence of substrings, separated by a given separator or delimiter. This task is accomplished by the following function, which breaks a string into pieces called "tokens" (hence the abbreviation strtok):

char * strtok(char * strptr, const char * delimiter);

Using strtok is done in two stages. The first call to strtok returns the first string before the given delimiter. For subsequent calls, a null string is used for the strptr parameter, and these calls return the next elements in the sequence. When no more elements are present, strtok returns NULL.

Warnings:

The following example places the months given above into an array of 12 string pointers:

char year[] = "January,February,March,April,May,June,July,"
  "August,September,October,November,December" ;

/* copy original array */  
char* yearCopy = malloc (sizeof(char)*(strlen(year) + 1));
strcpy (yearCopy, year);

/* array to receive the string pieces, including space for NULL strtok at end */
char* months[13];

/* loop puts strings into successive elements of months array */
months[0] = strtok(yearCopy, ",");

i = 1;
while ((months[i] = strtok(NULL, ","))) /* continue as long as token != NULL */
    i++;
printf ("result of using strtok to break up a string of months\n");
for (i = 0; i < 12; i++)
  printf ("\t month %d: %s\n", i, months[i]);
printf ("original year array:  %s\n", year);

Conversion to numbers

Input from a command line (and input from scanf using %s format) is a string. It can be a challenge to convert a string of digits to a number; so the stdlib.h library contains several functions to convert strings to numbers:

Examples
  Function call     Result     Comments  
atoi("1234") 1234 int type returned
atoi("3.14") 3 int type returned; digits after the decimal point ignored
atof("1234") 1234.0 double type returned
atof("3.14") 3.14 double type returned

Note that more recent (C99) library functions strtol and strtod feature more robust error checking and are to be preferred over atoi and atof.

Command-string processing and robot controls

We might combine string-processing and the command-line arguments into a method for dictating actions to the robot. Toward this end, our command-line arguments will be character-number pairs where the leading character would indicate some command, and the trailing number would be a single numeric parameter for that command. For instance, the argument f2 might signifiy moving forward for two seconds.

The following code exerpt may accomplish this by iterating over each command-line argument, extracting the first character of the string, then converting the subsequent characters in the string to its numeric equivalent.

  /* robot commands */
  printf ("robot commands entered from the command line\n");
  printf ("\t this program:  %s\n", argv[0]);

  
  for (int i = 1; i < argc; i++)
  {
    printf ("\t full robot command: %s\n", argv[i]);
    
    /* process 1-character robot commands */
    if (strlen(argv[i])>= 2 && isalpha (argv[i][0]) && isdigit (argv[i][1]))
    {
      char command1 = argv[i][0];
      int value1 = atoi (argv[i]+1);
      printf ("\t\t command letter: %c\n", command1);
      printf ("\t\t integer value: %d\n", value1);
    }
  }