START OF WEEK 5 of C <<< FILE TIMESTAMP >>> -The up to date file is in: /usr/courses/cps393/dwoit/courseNotes/ -If you are viewing your own copy, check it is up to date -If you are viewing from a link in the Course Outline, be aware it may be outdated. <<< Function Pointers >>> we can declare a pointer to a function the pointer points to code, not data (that code is normally the start of the executable code of a function) we don't allocate memory for a function pointer because what it points to already exists (function code) //source: funcPtr.c #include #include //a basic function f void f (int a, int b) { printf("function f says a=%d and b=%d\n",a,b); } int main (void) { //fPtr is a POINTER to a function. That function to which //it points has 2 int parameters and returns void void (*fPtr)(int,int); //function f has right type (2 int params and returns void) //so make fPtr point to f fPtr=&f; //Call f using fPtr (*fPtr)(5,8); exit(0); } A function's name actually IS its address, so don't even neeed the '&' in above program. e.g., this line: fPtr=&f; could be this: fPtr=f; this line: (*fPtr)(5,8); could be this: fPtr (5,8); code in funcPtrNN.c does this <> Just like any pointer, can have an array of them, e.g.: //source: funcPtrArray.c #include #include int add(int a, int b) { return (a+b); } int sub(int a, int b) { return (a-b); } int mul(int a, int b) { return (a*b); } int main (void) { //fpA is array of function pointers int (*fpA[])(int,int) = {add,sub,mul}; printf("2+5=%d\n", (*fpA[0])(2,5)); printf("2-5=%d\n", (*fpA[1])(2,5)); printf("2*5=%d\n", (*fpA[2])(2,5)); // or this, if you don't like to see the explicit pointer // printf("2+5=%d\n", fpA[0](2,5)); // printf("2-5=%d\n", fpA[1](2,5)); // printf("2*5=%d\n", fpA[2](2,5)); exit(0); } <> Just like any pointer, it can be passed as an argument //source: funcPtrArg1.c #include #include //basic function prints sum of its arguments void sum (int a, int b) { printf("%d",a+b); } //basic function prints product of its arguments void prod (int a, int b) { printf("%d",a*b); } //a function that takes a basic function as a parameter //and calls that basic function void apply(int x, int y, void (*f)(int,int)) { (*f)(x,y); } int main (void) { apply(2,4,sum); printf("\n"); apply(2,4,prod); printf("\n"); exit(0); } <> Just like any pointer, it can be returned from a function //source: funcPtrArg2.c #include #include int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } //getFunc takes one char argument. //getFunc returns a pointer to a function (that returns //an int and takes 2 int arguments) int (*getFunc(char op))(int,int) { //or //typedef int (*FPTR)(int, int); //FPTR getFunc(char op) { if (op == '+') return &add; else if (op == '-') return ⊂ else if (op == '*') return &mul; else return NULL; } int main(void) { int (*func)(int, int); //or FPTR func; int a = 3; int b = 5; int r; char c; c='+'; func = getFunc(c); r = (*func)(a, b); printf("%d %c %d = %d\n", a, c, b, r); c='*'; func = getFunc(c); r = (*func)(a, b); printf("%d %c %d = %d\n", a, c, b, r); return 0; } <<< External Storage Specifier >>> -extern can be used for functions or variables to extend their visibility in the program Aside: cannot be used in a struct declaration, but could be used for a variable whose type is struct xyz if needed -recall: -a variable/function DECLARATION says variable/function exists somewhere in this program, but no storage/memory is allocated -a variable/function DEFINITION does same as declaration PLUS allocates memory/storage -a variable/function may be declared multiple times in program, but can be defined only once << extern in Functions >> -Function declaration is its prototype, and definition is its code -Nothing special needed to make functions extern, since extern already implicit in function declarations -So when compiler sees this: int x (int arg1, char arg2); it treats it as if it were this: extern int x (int arg1, char arg2); Thus, function x is visible to whole program -so it can be called from ANYWHERE in ANY file of the program (as long as the file with the call also has a declaration of function x.) -extern not necessary for functions, but often used anyway, to remind user that function is defined elsewhere, e.g., see printf in /usr/include/stdio.h << extern Variables >> -extern NOT implicit for variables -note that when code has this, x is both declared and defined int x; -to declare x WITHOUT defining it (without allocating storage): extern int x; -this is a reference to a variable of the same name DEFINED in another file. So x is just a name--it has no storage. Can compile this file with gcc -c. If want to run it, compile along with another file that defines x. -initializing an extern variable IS its definition -note: if you had program in a single file, extern unnecessary, as could just make x global. But if need x in multiple files, need extern. ext1.c compiles and runs fine; x is defined (and declared) as global to whole program: //source: ext1.c //global x #include int x; int main(void) { printf("x=%d\n",x); return 0; } ext2.c compiles and runs; x is declared (not defined) so can't be used //source: ext2.c #include extern int x; int main(void) { //printf("x=%d\n",x); return 0; } ext3.c won't compile since x has no storage allocated and variable x does not exist in the program //source: ext3.c //this won't compile #include extern int x; int main(void) { x=5; printf("x=%d\n",x); return 0; } ext4.c is fine since ext.h defines x //source: ext4.c #include #include "ext.h" extern int x; int main(void) { x=5; printf("x=%d\n",x); return 0; } //source: ext.h int x; ext5.c compiles with a warning, and runs extern only declares x, but adding an initializer at that point also allocates storage. However, compiler warning, since extern really only declares //source: ext5.c #include extern int x=2; int main(void) { x=5; printf("x=%d\n",x); return 0; } << extern to Get Global Variables in Multiple Files >> -typical method is to use extern in a header file -.h file included by all files needing global variable -one (and only one) of those files also must define the variable (initialize it) -e.g., global variable x is shared among globMain.c, glob[12].c make -f globMakefile to make executable glob //source: globMain.c #include "globVars.h" #include "globFuncs.h" #include int main(void) { showx(); x = x+20; printf("did x+20\n"); showx(); addone(); printf("did addone\n"); showx(); return 0; } //source globVars.h extern int x; //declaration //source: globFuncs.h void addone(void) ; void showx(void) ; //source: glob1.c #include "globVars.h" //variable declaration int x = 5; //variable definition (must match declaration) void addone(void) { x=x+1; } //source: glob2.c #include "globVars.h" //variable declaration #include void showx(void) { printf("global var x=%d\n",x); } Can we make a variable global in ONE file only, and hidden to all other files? Yes. make it static in that file. But can't make it global in, say, 2 files, but still hidden to all other files. e.g., -suppose F.c, wants variable x global to F.c and G.c -so F.c declares/defines x near top of file, outside any function -However, if F.c is compiled along with M.c, then M.c can access F.c's global x if M.c declares: extern int x; -e.g., -M.c below grabs F.c's global x using: extern int x; -Note that if M.c instead did: int x; then x would be different variable (only local to main) -with "extern int x" uncommented this prints: A says x=1 B says x=19 //M changed F's x main says x=19 -with "int x" uncommented this prints: A says x=1 B says x=2 //M could not change F's x main says x=19 //source: F.c #include int x; void A(void) { x=1; printf("A says x=%d\n",x); x=2; } //source: G.c //compile with M.c F.c #include extern int x; void B(void) { printf("B says x=%d\n",x); } //source: M.c #include void A(void); void B(void); int main (void) { extern int x; //F.c's global x //int x; //local (to main) x A(); x=19; B(); printf("main says x=%d\n",x); } If x were declared static in F.c, then M.c can't access it. (won't compile). But neither could G.c access it. <<< Variadic (or Vararg) Functions >>> -function takes a variable number of arguments, like printf, scanf -not part of C language, but works via macros defined in stdarg.h -in program below, variadic function sumargs: -takes variable number of arguments -first arg is a count of how many args remain -returns sum of all but first arg - sumargs(2, 5, 3) returns 8 - sumargs(4, 2, 1, 2, 10) returns 15 -these are given: va_arg, va_start, va_end, va_list (see Variadic Arguments below) -ap is typical variable name for the va_list (see Variadic Arguments below) -every va_start must have matching va_end in same function. Required. can have multiple traversals of arg list, each enclosed in its own va_start and va_end //source: var1.c #include #include int sumargs (int count,...) { va_list ap; int i, total; va_start(ap, count); //initialize argument list //which starts after argument named count total = 0; for (i = 0; i < count; i++) total = total + va_arg(ap, int); //get next argument va_end(ap); //cleanup for ap. Required. return total; } int main (void) { printf ("%d\n", sumargs (4, 2, 1, 2, 10)); //15 printf ("%d\n", sumargs (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); //55 printf ("%d\n", sumargs (0)); //0 return 0; } << Variadic Arguments >> -prototype must have one or more fixed arguments, specified in the usual way, followed by optional arguments, specified by, literally: ... -the fixed arguments each have a type, and access them as usual -can access the optional arguments one-by-one, in order given How: -use va_start to initilize an argument pointer variable of type va_list (variable typically named ap) -va_start's second argument must be name of last fixed argument in function's argument list e.g., if had this: int F (int x, int y, ...) then need this: va_start(ap,y) -use va_arg to get next optional argument -can stop any time (don't need to iterate through whole list) but if go too far (past list end) get junk -use va_end to indicate no longer need the argument pointer variable you initialized with va_start (system will clean up if you forget). -may pass ap to a function, and loop through it in that function instead of function that did va_start -can initialize multiple argument pointer variables if want to: -initialize each with own va_start (and kill it with own va_end) -each pointer still iterates through the same argument list, but at its own pace << Number and Types of Arguments >> -a Variadic function can't figure out the number and type of arguments it was called with. So, -function DESIGNER must provide some kind of RULE for user to follow to help function figure it out. -typical kinds of rules: -one fixed argument which specifies the count of the optional arguments (as in var1.c above) -a sentinal as last optional argument. e.g., if the optional args are pointers, the last argument could be NULL to indicate end of argument list -one fixed argument can contain a pattern indicating the number and types of the optional arguments. e.g., the format specifier string of printf does this. Promotions: -variadic function's optional arguments are subject to DEFAULT ARGUMENT PROMOTIONS. -char and short int promoted to int -unsigned short int promoted to unsigned int -float is promoted to double -e.g., if arg type char is passed, variadic function accesses it with va_arg (ap, int) var2.c shows -the float arguments passed to sumargs get promoted to double -use of a string as required argument to help determine number and type of optional arguments //source: var2.c #include #include #include double sumargs (char *str,...) { va_list ap; double tot=0; int k; //number of optional arguments int T; //type: 0 for in1 1 for double va_start(ap, str); //initialize argument list if ((strcmp(str,"2ints"))==0) {T=0; k=2;} else if ((strcmp(str,"3ints"))==0) {T=0; k=3;} else if ((strcmp(str,"2doubles"))==0) {T=1; k=2;} else if ((strcmp(str,"3doubles"))==0) {T=1; k=3;} if (T==0) //type int for (int i=0; i