0. Announcements
- The dollar sign ($) is not valid in C variable names. However, as some of you noted, it is permitted by some C compilers. Why is this? It turns out that some compilers permit it so that C can call functions written in other languages. In your homework, you were given full credit whether you said $ was valid or not.
- Almost everyone did better on Friday's quiz (SQ3), than prior quizzes. The low was 7.5, high was 10, and mean was 9.4.
- Programming Assignment #3 is now assigned, and is due Tuesday February 15.
1. main()'s argc and argv arguments
Now that we've seen arrays and pointers and how similar they are, we can figure out how the arguments to the required main() function work. The typical declaration for main is:The first parameter, argc is simply the count of the arguments to the program. The second paramter has the argment values. Note that the order of those parameters is always argc followed by argv.int main(int argc, char *argv[])Now argv is a form we haven't seen before. It is declared to be an array of character pointers. In fact, we'll treat it as an array of strings. Note that we could equivalently declare it to be a pointer to a character pointer (char **argv). Each string contains one argument, where each argument to the program is separated by whitespace (one or more spaces or tabs). Note that the very first entry in argv[] is a pointer to the name of the program that was executed.
Exercise 1. Write a simple program that prints out each argument, one per line, as in:
% ./printargs this is a test. Program name: ./printargs Number of arguments: 5 Arg 1: this Arg 2: is Arg 3: a Arg 4: test.2. Pointer Fun
In our last lecture, we discussed strings and pointers. If you have not yet reviewed the last slide of the lecture (which we didn't quite finish during class), please do so now. That slide talks about modifying static strings and the problems it can cause.
Static Strings
In this lab, we will explicitly do some of the things that we should not normally do. What does that mean? Well, for one, we will attempt to modify static strings and see what happens.Exercise 2. Compile and run the following program:
Then remove the comment markers to allow the string2[0] assignment, and compile and run it again. What was the result?int main(void) { char string1[] = "Hello 1"; char *string2 = "Hello 2"; string1[0] = 'J'; // string2[0] = 'J'; return 0; }
Local and non-local variables
Variables that are declared within a block of code, such as the char string1[] array in the main() function above are called automatic local variables. They are automatic because they are created automatically when the function is executed, and they are local because their scope is only within the same block as their declaration.Exercise 3. Compile and run the testlocal.c program. In this example, a pointer from a local variable is returned from a function. It turns out, in cases 1 and 2, that the local variable happens to be accessible even after the function has completed. However, we find in cases 3 and 4 that the value in memory is overwritten with the value of a later function. In case 3 the new value is recognizable but not in case 4.
We almost always use automatic local variables in our programs, but there are alternatives. One is to use global variables -- these are variables declared outside of any function, and are thus accessible anywhere within a program. Most of the time it is good programming practice to avoid using global variables. Another alternative is to declare a local variable with the additional static keyword.
When static is applied to an automatic local variable, that variable is is no longer automatically created whenever the function is executed. Instead, the variable is created (and initialized) just once, but is available for use every time the function is executed. It is still local in that no other function has access to the variable, though.
When static is applied to a global variable, that variable is no longer accessible to code outside of the current source file. This is useful when the variable is not meant to be accessible outside of this set of functions (data hiding).
Similarly, when static is applied to a function declaration, the function name is no longer available to any other function outside of the current source file. Again, this helps enforce modularity and data hiding.
Constant variables and parameters
If you've kept up with your readings, you saw the const qualifier which allows you to specify to the compiler those variables that will always have a constant value throughout execution. The typical example is for the value of pi:const double pi = 3.141592654;The variable pi will now be considered to be read-only, and the compiler will warn you of any use that would attempt to write to that variable. The same use of const can be applied to other variable types, including arrays.We can also apply the const qualifier to pointers, but now we need to consider whether they apply to the pointer or to the data to which it points. That is, we can declare "constant pointers" or "pointers to constant data", or both, as in:
int i = 4; int * const const_pointer = &i; const int *pointer_to_const = &i; const int * const const_pointer_to_const = &i;Exercise 4: To test your skills in pointers, write a program that has two local integer variables with different values, prints out the values, and then calls a function that swaps the values of the two variables, and after it returns, the main prints out the new values of the variables. Extend your implementation to use constant pointers as parameters to your swap function (preventing your function from changing the pointers).
Pointers to functions
Sometimes it is useful to keep track of functions dynamically (rather than, say, a variable and an extended switch() statement). C lets us create pointers to functions. When we declare such a pointer, we need to know not only that it is a pointer to a function, but also the return type and parameter types of the functions that the variable might point to. For example, to declare a variable fnPtr to be of type "pointer to function that returns a char * and takes an int as a single argument", we would use:char * (*fnPtr)(int);Later we can assign fnPtr to a value, by using the name of a function that has already been declared:fnPtr = myfunction;where myfunction is exactly of the type "returns a char pointer and takes a single int as argument". We can then call the function using the variable like any other function:char *c; c = fnPtr(4);That's it; feel free (using what you learned from our first lab) to print some (small) file to the printer if you wish to test your ability to print before you go.