# IC221: Systems Programming (SP16)

Home Policy Calendar Resources

# Lecture 07: Double Arrays, Command Line Arguments, and Error Checking

## 1 Double Arrays

We continue our discussion of data types in C by looking at double arrays, which is an array of arrays. This will lead directly to command line arguments as these are processed as an array of strings, which are arrays themselves, thus are double arrays.

### 1.1 Declaring Double Arrays

Like single arrays, we can declare double arrays using the `[ ]`, but with two. We can also do static declarations of values with ```{ }```. Here's an example:

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

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

int darray[][4] = { {0, 0, 0, 0},
{1, 1, 1, 1},
{2, 2, 2, 2},
{3, 3, 3, 3}};
int i,j;

for(i=0;i<4;i++){
printf("darray[%d] = { ",i);
for(j=0;j<4;j++){
printf("%d ",darray[i][j]); //<---
}
printf("}\n");
}

}
```
```aviv@saddleback: demo \$ ./int_doublearray
darray[0] = { 0 0 0 0 }
darray[1] = { 1 1 1 1 }
darray[2] = { 2 2 2 2 }
darray[3] = { 3 3 3 3 }
```

Each index in the array references another array. Like before we allow C to determine the size of the outer array when declaring statically. However, you must define the size of the inner arrays. This is because of the way the memory is allocated. While the array example above is square in size, the double array can be asymmetric.

### 1.2 The type of a double array

Let's think a bit more about what a double array really is given our understanding the relationship between pointers and arrays. For a single array, the array variable is a pointer that references the first item in the array. For a double array, the array variable references a reference that references the first item in the first array. Here's a visual of the stack diagram:

```                           .---.---.---.---.
.---.  _.----> | 0 | 0 | 0 | 0 |  <-- darray[0]
darray ---> | --+-'        '---'---'---'---'
|---|          .---.---.---.---.
| --+--------> | 1 | 1 | 1 | 1 |  <-- darray[1]
|---|          '---'---'---'---'
| --+-._       .---.---.---.---.
|---|   '----> | 2 | 2 | 2 | 2 |  <-- darray[2]
| --+-._       '---'---'---'---'
'---'   '._    .---.---.---.---.
'-> | 3 | 3 | 3 | 3 |  <-- darray[3]
'---'---'---'---'
```

If we follow the arrays, we see that the type of `darray` is actually a `int **`. That means, it is a pointer that references a memory address that stores another pointer that references a memory address of an integer. So when we say double array, we are also referring to double pointers.

To demonstrate this further, we can even show the dereferences directly.

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

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

int darray[][4] = { {0, 0, 0, 0},
{1, 1, 1, 1},
{2, 2, 2017, 2},
{3, 3, 3, 3}};

printf("*(*(darray+2)+2) = %d\n", *(*(darray+2)+2));
printf("     daray[2][2] = %d\n", darray[2][2]);
}
```
```aviv@saddleback: demo \$ ./derefernce_doublearray
*(*(darray+2)+2) = 2017
daray[2][2] = 2017
```

As you can see it takes two dereferences to get to the integer value.

### 1.3 Array of Strings as Double Arrays

Now let us consider another kind of double array, an array of strings. Recall that a C string is just an array of characters, so an array of strings is a double array of characters. One of the tricky parts of string arrays is the typing declaration.

Before we declared arrays using the following notation:

```char str1[] = "This is a string";
```

But now we know that the type of arrays and pointers are really the same, so we can also declare a string this way:

```char * str2 = "This is also a string";
```

Note that there is a difference between these two declarations in how and where C actually stores the string in memory. Consider the output of this program:

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

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

char str1[] = "This is a string";
char * str2 = "This is also a string";

printf("str1: %s \t\t &str1:%p\n",str1,str1);
printf("str2: %s \t &str2:%p\n",str2,str2);

}
```
```aviv@saddleback: demo \$ ./string_declare
str1: This is a string 		 &str1:0x7fff4344d090
str2: This is also a string 	 &str2:0x4006b4
```

While both strings print fine as strings, the memory address of the two strings are very different. One is located in the stack memory region and other is in the data segment. In later lessons we will explore this further, but for the moment, the important takeaway is that we can now refer to strings as `char *` types.

Given we know that `char *` is the type of a string, then array of `char *`'s is an array of strings.

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

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

char * strings[]={"Go Navy!",
"Beat Army!",
"Crash Airforce!",
"Destroy the Irish!"};
int i;

printf("strings: %p\n",strings);
for(i=0;i<4;i++){
printf("strings[%d]: '%s' %p\n",i,strings[i],strings[i]);
}
}
```
```aviv@saddleback: demo \$ ./string_array
strings: 0x7fff7af68080
strings[0]: 'Go Navy!' 0x400634
strings[1]: 'Beat Army!' 0x40063d
strings[2]: 'Crash Airforce!' 0x400648
strings[3]: 'Destroy the Irish!' 0x400658
```

Like before we can see that `strings` is a pointer that references a pointer to a `char`, but that's just an array of strings or a double array. Another thing you may notice is that the length and size of each of the strings is different. This is because the way the array is declared with `char *` as the type of the string rather than ```char []``` which changes how the array is stored.

## 2 Command Line Arguments

Now that you have seen an array of strings, where else does that type appear? In the arguments to the `main()` function. This is part of the command line arguments and is a very important part of systems programming.

In your previous classes you have only accepted input from the user by reading from standard in using `cin`. While we will also use standard input, this class will require reading in more input from the user in the form of command line arguments. These will be used as basic settings for the program and are much more efficient than always reading in these settings from standard input.

You may recall that we already did some work with command line arguments. First we discussed the varied command line arguments for the UNIX command line utilities, like `ls` and `cut` and `find`. We also processed some command line arguments from

### 2.1 Understanding `main()` arguments

You may have notices that I have been writing main functions slightly differently then you have seen them before.

```//
//argument ____.             ._____ argument
// count       |             |       variables
//             v             v
int main(int argc, char * argv[]);
```

The arguments to main correspond to the command line input. The first is the number of such arguments, and the second is a string array of the argument values. Here's an example that illuminates the arguments:

```/*print_args.c*/
#include <stdio.h>
#include <stdlib.h>

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

for(i=0;i<argc;i++){
printf("argv[%d] = %s\n",i,argv[i]);
}
}
```
```aviv@saddleback: demo \$ ./print_args arg1 arg2 arg3 x y z adam aviv
argv[0] = ./print_args
argv[1] = arg1
argv[2] = arg2
argv[3] = arg3
argv[4] = x
argv[5] = y
argv[6] = z
argv[8] = aviv
```

Looking at the program and its output, you can see that there is correspondence to the arguments provided and the index in the array. Its important to note that the name of the program being run is `arg[0]`, which means that all programs have at least one argument, the name of the program. For example:

```aviv@saddleback: demo \$ ./print_args
argv[0] = ./print_args
```

The name of the program is not compiled into the executable. It is instead passed as a true command line argument by the shell, which forks and executes the program. The mechanics of this will become clear later in the semester when we implement our own simplified version of the shell. To demonstrate this now, consider how the `arg[0]` changes when I change the name of the executable:

```aviv@saddleback: demo \$ cp print_args newnameofprintargs
argv[0] = ./newnameofprintargs
aviv@saddleback: demo \$ ./newnameofprintargs a b c d e f
argv[0] = ./newnameofprintargs
argv[1] = a
argv[2] = b
argv[3] = c
argv[4] = d
argv[5] = e
argv[6] = f
```

### 2.2`NULL` Termination in args arrays

Another interesting construction of the `argv` array is that the array is `NULL` terminated much like a string is null terminated. The reason for this is so the OS can determine how many arguments are present. Without null termination there is no way to know the end of the array.

You can use this fact when parsing the array by using pointer arithmetic and checking for a NULL reference:

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

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

char ** curarg;
int i;

for( curarg=argv , i=0 ; //initialize curarg to argv array and i to 0
*curarg != NULL;    //stop when curarg references NULL
curarg++, i++){     //increment curarg and i

printf("argv[%d] = %s\n", i, *curarg);
}

}
```
```viv@saddleback: demo \$ ./print_args_pointer a b c d e
argv[0] = ./print_args_pointer
argv[1] = a
argv[2] = b
argv[3] = c
argv[4] = d
argv[5] = e
```

Notice that the pointer incrementing over the `argv` arrays is of type `char **`. Its a pointer to a string, which is itself an array of chars, so its a pointer to a pointer. (POINTERS ARE MADNESS!)

### 2.3 Basic Parsing of Command Line Arguments: `atoi()` and `sscanf()`

Something that you will often have to do when writing programs is parse command line arguments. The required error checking procedure can be time consuming, but it is also incredibly important for the overall user experience of your program.

Lets consider a simple program that will print a string a user specified number of times. We would like to execute it this way:

```run_n_times 5 string
```

Where `string` is printed n times. Now, what we know about command line arguments is that they are processed as strings, so both `string` and `5` are strings. We need to convert "5" into an integer 5.

There are two ways to do this. The first is `atoi()` which converts a string to a number, but looking at the manual page for `atoi()` we find that `atoi()` does not detect errors. For example, this command line arguments will not be detected:

```run_n_times notanumber string
```

Executing, `atoi("notanumber")` will return 0, so a simple routine like:

```int main(int argc, char * argv[]){
int i;
int n = atoi(argv[1]); //does not detect errors

for(i=0;i<n;i++){
printf("%s\n",argv[2]);
}
}
```

will just print nothing and not return the error. While this might be reasonable in some settings, but we might want to detect this error and let the user know.

Instead, we can convert the `argv[1]` to an integer using `scanf()`, but we have another problem. We have only seen `scanf()` in the concept of reading from standard input, but we can also have it read from an arbitrary string. That version of `scanf()` is called `sscanf()` and works like such:

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

if( sscanf(argv[1],"%d", &n) == 0) ){
fprintf(stderr, "ERROR: require a number\n");
exit(1); //exit the program
}

for(i=0;i<n;i++){
printf("%s\n",argv[2]);
}
}
```

Recall that `scanf()` returns the number of items that successfully match the format string. So if no items match, then the user did not provide a number to match the `%d` format. So this program successfully error checks the first argument. But, what about the second argument? What happens when we run with these arguments?

```./run_n_times 5
```

There is no `argv[2]` provided and worse because the `argv` array is NULL terminated, `argv[2]` references `NULL`. When the `printf()` derferences `argv[2]` it wlll cause a segmentation fault. How do we fix this? We also have to error check the number of arguments.

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

if(argc < 2){
fprintf(stderr, "ERROR: invalid number of arguments\n");
exit(1); //exit the program
}

if( sscanf(argv[1],"%d", &n) == 0) ){
fprintf(stderr, "ERROR: require a number\n");
exit(1); //exit the program
}

for(i=0;i<n;i++){
printf("%s\n",argv[2]);
}
}
```

And now we have properly error checked user arguments to this simple program. As you can see, error checking is tedious but incredibly important.