Start Lecture #11
On the right is a picture of how arguments are passed to a (Unix) command. Each main() program receives two arguments an integer, normally called argc for argument count, and an array of character pointers, normally called argv for argument vector.
The diagram shows argv as an array and the code below treats it that way as well. As always, an array name is also a pointer to the first element. If you view argv as a pointer, then you would draw a box for it with an arrow pointing to the array. The book pictures it that way.
#include <stdio.h> int main(int argc, char *argv[argc]) { int i; printf("My name is %s.\n", argv[0]); printf("I was called with %d argument%s\n", argc-1, (argc==2) ? "" : "s"); for (i=1; i<argc; i++) printf("Argument #%d is %s.\n", i, argv[i]); }
sh-4.0$ cc -o cmdline cmdline.c sh-4.0$ ./cmdline My name is ./cmdline. I was called with 0 arguments. sh-4.0$ ./cmdline x My name is ./cmdline. I was called with 1 argument. Argument #1 is x. sh-4.0$ ./cmdline xx y My name is ./cmdline. I was called with 2 arguments. Argument #1 is xx. Argument #2 is y. sh-4.0$ ./cmdline -o cmdline cmdline.c My name is ./cmdline. I was called with 3 arguments. Argument #1 is -o. Argument #2 is cmdline. Argument #3 is cmdline.c. sh-4.0$ cp cmdline mary-joe sh-4.0$ ./mary-joe -o cmdline cmdline.c My name is ./mary-joe. I was called with 3 arguments. Argument #1 is -o. Argument #2 is cmdline. Argument #3 is cmdline.c.
Since the same program can have multiple names (more on that later), argv[0], the first element of the argument vector, is a pointer to a character string containing the name by which the command was invoked. Subsequent elements of argv point to character strings containing the arguments given to the command. Finally, there is a NULL pointer to indicate the end of the pointer array.
The integer argc gives the total number of (valid) pointers, including the pointer to the name of the command. Thus, the smallest possible value for argc is 1 and argc is 3 for the picture drawn above.
The code on the right shows how a program can access its name and any arguments it was called with.
Having both a count (argc) and a trailing NULL pointer (argv[argc]==NULL) is redundant, but convenient. The code I wrote treats argv as an array. It loops through the array using the count as an upper bound. Another style would use something like
while (*argv) printf("%s\n", (*argv++));which treats argv as a pointer and terminates when argv points to NULL.
The second frame on the right shows a session using the code directly above it.
Now we can get rid of some symbolic constants that should be specified at run time.
Here are some before and after examples. The code on the left uses symbolic constants; on the right we use command line arguments
#include <stdio.h> #define LO 0 #define HI 300 #include <stdio.h> #define INCR 20 #include <stdlib.h> main() { int main (int argc, char *argv[argc]) { int F; int F; for (F=LO; F<=HI; F+=INCR) for (F=atoi(argv[1]); F<=atoi(argv[2]); F+=atoi(argv[3])) printf("%3d\t%5.1f\n", F, printf("%3d\t%5.1f\n", F, (F-32)*(5.0/9.0)); (F-32)*(5.0/9.0)); } return 0; }
abnormally(it doesn't return 0).
#include <stdio.h> #include <math.h> #define A +1.0 // should read #include <stdio.h> #define B -3.0 // A,B,C #include <math.h> #define C +2.0 // using scanf() #include <stdlib.h> void solve (float a, float b, float c); void solve (float a, float b, float c); int main() { int main(int argc, char *argv[argc]) { solve(A,B,C); solve(atof(argv[1]), atof(argv[2]), atof(argv[3])); return 0; return 0; } } void solve (float a, float b, float c) { void solve (float a, float b, float c) { float d; float d; d = b*b - 4*a*c; d = b*b - 4*a*c; if (d < 0) if (d < 0) printf("No real roots\n"); printf("No real roots\n"); else if (d == 0) else if (d == 0) printf("Double root is %f\n", -b/(2*a)); printf("Double root is %f\n", -b/(2*a)); else else printf("Roots are %f and %f\n", printf("Roots are %f and %f\n", ((-b)+sqrt(d))/(2*a), ((-b)+sqrt(d))/(2*a), ((-b)-sqrt(d))/(2*a)); ((-b)-sqrt(d))/(2*a)); } }
don't check the arguments. Now we specify them correctly.
#include <stdio.h> #include <ctype.h> int main (int argc, char *argv[argc]) { int c, makeUpper=0; if (argc > 2) return argc; if (argc == 2) if (strcmp(argv[1], "-toupper")){ printf("Argument %s is illegal.\n", argv[1]); return -1; } else makeUpper=1; while ((c = getchar()) != EOF) if (!isdigit(c)) { if (isalpha(c) && makeUpper) c = toupper(c); putchar(c); } return 0; }
Often a leading minus sign (-) is used for optional command line
arguments.
The program on the right removes all digits from the input.
If it is given the argument -toupper
it also converts all
letters to upper case using the toupper() library routine.
BooleanmakeUpper.
Demo this function on ajglap.cs.nyu.edu.
Homework: Combine the entab and detab functions by writing a function tab that has one argument
tab -en # performs like entab tab -DE # performs like detabUse a reasonable default for tab width. No need to support the user specifying the tab width, i.e., always use the default value.
#include <ctype.h> #include <string.h> #include <stdio.h> // Simple program to illustrate function pointers int digitToStar(int c); // Convert all digits to * int letterToStar(int c); // Convert all letters to * int main (int argc, char *argv[argc]) { int c; int (*funptr)(int c); if (argc != 2) return argc; if (strcmp(argv[1],"digits")==0) funptr = &digitToStar; else if (strcmp(argv[1],"letters")==0) funptr = &letterToStar; else return -1; while ((c=getchar())!=EOF) putchar((*funptr)(c)); return 0; } int digitToStar(int c) { if (isdigit(c)) return '*'; return c; } int letterToStar(int c) { if (isalpha(c)) return '*'; return c; }
In C you can do very little with functions, mostly define them and call them (and take their address, see what follow).
However, pointers to functions (called function pointers) are real values.
The program on the right is a simple demonstration of function pointers. Two very simple functions are defined.
The first function, digitToStar() accepts an integer (representing a character) and return an integer. If the argument is a digit, the value returned is (the integer version of) '*'. Otherwise the value returned is just the unchanged value of the argument.
Similarly letterToStar() convert a letter to '*' and leaves all other characters unchanged.
The star of the show is funptr. Read its declaration carefully: The variable funptr is the kind of thing that, once de-referenced, is the kind of thing that, once given an integer, is an integer.
So it is a pointer to something. That something is a function from integers to integers.
The main program checks the (mandatory) argument. If the argument is "digits", funptr is set to the address of digitToStar(). If the argument is "letters", funptr is set to the address of letterToStar().
Then we have a standard getchar()/putchar() loop with a slight twist. The character (I know it is an integer) sent to putchar() is not the naked input character, but instead is the input character processed by whatever function funptr points to. Note the "*" in the call to putchar().
Note: C permits abbreviating &function-name to function-name. So in the program above we could say
funptr = digitToStar; funptr = letterToStar;instead of
funptr = &digitToStar; funptr = &letterToStar;I don't like that abbreviation so I don't use it. Others do like it and you may use it if you wish.
One difference between a function pointer and a function is their size. A big function is big, a small function is small, and an enormous function is enormous. However all function pointers are the same size. Indeed, all pointers in C are the same size. This makes them easier for the system to deal with.
We are basically skipping this section.
It shows some examples more complicated than we have seen (but are
just more of the same
—one example is below).
The main part of the section presents a program that converts C
definition to/from more-or-less English equivalents.
Here is one example of a complicated declaration. It is basically the last one in the book with function arguments added.
char (*(*f[3])(int x))[5]
Remembering that *f[3] (like *argv[argc] is an array of pointers not a pointer to an array, we can unwind the above to.
The variable f is an array of size three of pointers.
Remembering that *(g)(int x) = *g(int x) is a function returning a pointer and not a pointer to a function, we can further unwind the monster to.
The variable f is an array of size three of pointers to functions taking an integer and returning a pointer to an array of size five of characters.
One more (the penultimate from the book).
char (*(f(int x))[5])(float y)
The function f takes and integer and returns a pointer to an array five pointers to functions taking a real and returning a character.