25
C : defining functions

C functions.pdf

Embed Size (px)

Citation preview

  • C : defining functions

  • With the exception of arrays and functions , C always passes arguments `by value': a copy of the value of each argument is passed to the function; the function cannot modify the actual argument passed to it:

    void foo(int j) { j = 0; /* modifies the copy of the argument received by the function */ }

    int main(void) { int k=10; foo(k); /* k still equals 10 */ }

    If you do want a function to modify its argument you can obtain the desired effect using pointer arguments instead:

    void foo(int *j) { *j = 0; }

    int main(void) { int k=10; foo(&k); /* k now equals 0 */ }

    Creating functions

  • Creating functions pass by value int func(int x) { int i = 0 ; x = 100 ; printf("this is in the function\n"); i += x ; return i ; }

    int main( int argc, char ** argv ) { int y = 50; int x = func(y); printf("returned value of func(): %d\n",func(y)); printf("value of y : %d\n",y); return 0; }

    func(y) is not actually using the y directly, it's actually copying its value into the function. This called call by value and all functions in C are called by value. It's not actually passing a reference to the variable. So the function has no way of affecting this y. The value of y will still being 50. If the return type is void , void func(int x) , then it's no longer legal to return anything.

    Result : this is in the function this is in the function returned value of func(): 100 value of y : 50

  • Creating functions - pass by reference

    int func(int *x) { int i=0 ; *x = 100 ; printf("this is in the function\n"); i += *x ; return i ; } int main( int argc, char ** argv ) { int y = 50; int x = func(&y); printf("returned value of func(): %d\n",x); printf("value of y : %d\n",y); return 0; }

    If we want to be able to affect y , then what we have to do is to pass a pointer. Now the parameter is a pointer and what we are passing it s the address of a variable. So now we are actually able to affect this variable, the value of y will be 100. That's called pass by reference as opposed to pass by value. C is always pass by value, and we are still passing a value, it's just the value is now a pointer. And so that's how we are able to affect variables outside the function.

    Result :

    this is in the function

    returned value of func(): 100

    value of y : 100

  • Creating functions Declaring functions

    int func(int x) ; // declaration of the function int main( int argc, char ** argv ) { int y = 50; int x = func(&y); printf("returned value of func(): %d\n",x); printf("value of y : %d\n",y); return 0; } // definition of the function int func(int *x) { int i=0 ; *x = 100 ; printf("this is in the function\n"); i += *x ; return i ; }

    In order to properly use a function when it's used before its definition is to have a declaration. int func(int) ; Now it's common to include the name, because it gives a hint of how it's used int func(int x) ; We have told the compiler exactly what that function looks like, and that it will be defined later.

  • Creating functions header file

    Func.c file #include #include "func.h" int main( int argc, char ** argv ) { int y = 50; int x = func(&y); printf("returned value of func(): %d\n",x); printf("value of y : %d\n",y); return 0; } int func(int *x) { int i=0 ; *x = 100 ; printf("this is in the function\n"); i += *x ; return i ; }

    func.h file #ifndef FUNC_H_ #define FUNC_H_ int func(int) ; #endif /* FUNC_H_ */

    It is really common is that the function itself is actually in a separate translation unit, it's in a separate source file, and it's linked in later. And what's even more common is instead of having this function signature here to have it in a header file. That's the common way to do this, whatever functions you are using in a particular source file to have their function signatures in a header file.

  • Creating functions - libraries

    The common way is to have a set of functions in one source file, and have all of their function signatures in the header file, and we can compile that as a separate unit. And then when we want to use that and link to it, all of those function signatures are in the header file, and we can simply include that header file in our main program, and link to the code and everything will work the way that we want it to. So this is really the common way to do this, this is how libraries are created. In fact, this is why we include at the beginning of all our programs. Because functions like printf() and puts() that we use a lot are defined in. And then the actual code is linked in with our program as we build it. So that's how our standard libraries work, and that's really how all libraries work in C. So the function is the basic unit of code in C. Everything starts from the main function and all code must be in function blocks. Understanding functions is a fundamental skill for C and C++ programming.

  • Defining Functions call by value, call by reference

    #include int f(int a){ return ++a ; } int main() { int a = 1; printf (" f(a) is %d\n" , f(a) ); printf (" a is %d\n" , a ); return 0 ; }

    Call by value Call by reference

    In C and C++ arguments are always passed to functions by value. So when you call a function, a copy of the content of the variable is passed to the function. If the function then changes that value the caller's copy remains unchanged. For example, here we have a variable a, with the value 1, this variable is passed to a function f, after the function returns we print the value. The function f() takes that value and assigns it to a local variable also named a. But this is absolutely a

    different variable, it's in a different scope.

    #include int f(int *p){ return ++(*p) ; } int main() { int a = 1; f(&a) ; printf (" a is %d\n" , a ); return 0 ; }

    Result : a is 2

    Result : f(a) is 2 a is 1

    when a is incremented in the function only its local copy is affected

    f(&a), ampersand of the function call parameter means to pass a pointer to a, instead of the value of a. The Ampersand is called the reference operator or sometimes the address of operator.

  • Defining Functions signature , and call by reference type

    Call by reference type

    In C++ you may also use the reference type to implement call by reference. This makes call by reference appear more implicit, this feature is not available in C. In C++ functions are identified by their function signature. long volume( long a) is different from double volume (double c) Even the two functions have the same name. The return type, the name of the function, and the types of the function arguments are all combined to form the function signature. This function signature is used to identify the function. In C this distinction does not exist and these two functions would cause a name collision, because a function is identified only by its name in C.

    #include void f(int &p) { ++p ; } int main() { int a = 1; f(a) ; printf (" a is %d\n" , a ); return 0 ; }

  • Defining Functions passing parameters to a function Parameters are passed to functions by declaring them inside the parentheses of the function definition. So in C and C++ functions always passed by value, so what that means is that when we make this function call with the i in it, it actually makes a copy of that i and passes the copy into the function and inside the function the copy is called i and so this is a copy that's being used inside the function.

    #include using namespace std; void func(int ); int main( int argc, char ** argv ) { int i = 7 ; func(i); printf("i is %d\n", i); return 0; } void func(int i) { i = 132 ; printf("in func(), i is %d\n", i); } Result in func(), i is 132 i is 7

    Call by value Call by reference

    #include using namespace std; void func(int * ); int main( int argc, char ** argv ) { int i = 7 ; func(&i); printf("i is %d\n", i); return 0; } void func(int *i) { *i = 132 ; printf("in func(), i is %d\n", *i); } Result in func(), i is 132 i is 132

    pass an address

  • Defining Functions passing parameters to a function

    #include using namespace std; void func(int & ); int main( int argc, char ** argv ) { int i = 7 ; func(i); printf("i is %d\n", i); return 0; } void func(int &i) { i = 132 ; printf("in func(), i is %d\n", i); }

    Now if you're using C++ you can do this with references instead of pointers and the advantage and disadvantage of references is that this syntax is much simpler. So the semantic is different, the effect is the same. Function calls always passed by value, if you pass a reference or a pointer it's the value of the reference or the pointer that you're passing.

    in func(), i is 132 i is 132

  • Defining Functions passing parameters to a function

    #include using namespace std; void func(string & ); int main( int argc, char ** argv ) { string s = "hello main" ; func(s); printf("%s\n", s.c_str()); return 0; } void func(string &s) { s = "hello func()" ; cout

  • Defining Functions using automatic and static variables, stack

    Variables declared in a function default to automatic storage. Other storage options are available.

    #include using namespace std; void func(); int main( int argc, char ** argv ) { func(); func(); func(); return 0; } void func() { int i = 5; printf("this is func() , i is : %d\n", i++); } Result : this is func() , i is : 5 this is func() , i is : 5 this is func() , i is : 5

    We get three times 5, The reason for that is this is in temporary storage. Automatic storage is stored on the stack which is created fresh for each invocation of a function. So the value is not carried from one invocation to the other.

    If on the other hand we declare this as being static storage static int i = 5; The variable i will be incremented every time. Static storage is not stored on the stack. It's not temporary. It's persistent for life of the

    process. The value is carried from one invocation to another. Static storage is typically used for keeping state and for other purposes where you just need to keep the storage around.

  • Defining Functions using function pointers

    #include using namespace std; int f( int i) { cout

  • Defining Functions using function pointers

    #include void func(); int main( int argc, char ** argv ) { void (*fptr)() = func ; (*fptr)() ; return 0; } void func() { printf("this is func()\n"); }

    If we use void (*fptr)() = &func ; Instead of void (*fptr)() = func ; The result is the same !!!

  • Defining Functions using function pointers #include void a() { puts("this is a()"); } void b() { puts("this is b()"); } void c() { puts("this is c()"); } void d() { puts("this is d()"); } void e() { puts("this is e()"); } int jump( char * ); int prompt();

    // array of function pointers void (*funcs[])() = { a, b, c, d, e, NULL };

    int main( int argc, char ** argv ) { while(prompt()); puts("\nDone."); return 0; }

    int prompt() { puts("Choose an option:"); puts("");; puts("1. Function a()"); puts("2. Function b()"); puts("3. Function c()"); puts("4. Function d()"); puts("5. Function e()"); puts("Q. Quit."); printf(">> "); fflush(stdout); enum { bufsz = 16 }; // constant for buffer size static char response[bufsz]; // static storage for response buffer fgets(response, bufsz, stdin); // get response from console return jump(response); }

    int jump( char * s ) { char code = s[0]; if(code == 'q' || code == 'Q') return 0; // get the length of the funcs array int func_length; for(func_length = 0; funcs[func_length] != NULL; func_length++); int i = (int) code - '0'; i--; // list is zero-based

    if( ( i < 0 ) || ( i > 8 ) ) { puts("invalid choice"); return 1; }

    if(i < func_length) { funcs[i](); return 1; } else { puts("invalid choice"); return 1; } }

  • Defining Functions using function pointers

    int i = (int) code - '0'; This line code is just a little trick for converting a character into an integer. The first character of the response string, it simply subtracts the value of 0 of the ASCII character 0, and then it's got an integer, it's got a 0 based integer.

    The previous code, is called a jump table. This is a really simple way to implement a menu in a console-based application or to implement any kind of a jump table where you might want to be calling different functions based on a piece of data.

    #include int main( int argc, char ** argv ) { char code = '3'; int i = (int) code - '0'; printf(" %d\n", i) ; return 0; }

    Dec Hex Char

    48 30 0

    49 31 1

    50 32 2

    51 33 3

    52 34 4

    53 35 5

    54 36 6

    55 37 7

    56 38 8

    57 39 9

    ASCII table

  • Defining Functions using function pointers #include #include using namespace std; void a() { puts("this is a()"); } void b() { puts("this is b()"); } void c() { puts("this is c()"); } void d() { puts("this is d()"); } void e() { puts("this is e()"); } int jump( const string & ); int prompt(); vector funcs = { a, b, c, d, e }; int main( int argc, char ** argv ) { while(prompt()); puts("\nDone."); return 0; } int prompt() { cout
  • #include int func() ;

    main() { int (*ptr) () = func ; printf(" this is i %d\n", (*ptr) ()); }

    int func() { int a =3, b = 4, c =7 ; int i[] = { a , b , c } ; return i[0] ; }

    We can have variables ( a,b,c ) inside array variables i[]

    Defining Functions simple example

  • Defining Functions overloading function names

    A function signature includes the return type, the function name, and the type and number of parameters.

    #include using namespace std; // volume of a cube int volume( int s ) { cout

  • Defining Functions overloading operators with functions When you're dealing with simple built-in scale or values, it's usually pretty obvious what most operators will do, 2+2 will generally get you 4. But when you're dealing with classes in C++, it's not obvious at all. In fact, C++ operators don't work with classes unless you tell them how. For this purpose, you'll want to write your own code for some operators, this is called Operator Overloading. There are basically two ways to overload operators in C++, one is with class methods, the other is with functions.

    #include using namespace std; class A { int a; public: A ( int a ) : a(a) {}; int value() { return a; } }; int operator + (A & lhs, A & rhs ) { cout

  • Defining Functions defining a variable number of arguments

    For those times when you need a function that may take a varying number of arguments, C and C++ provide variadic functions. You notice we including a header called standard arg , stdarg.h. In C++, the equivalent header is cstdarg.h. ( this technique works in both C and C++).

  • Defining Functions defining a variable number of arguments #include #include double average(const int count, ...) { va_list ap; int i; double total = 0.0; va_start(ap, count); for(i = 0; i < count; i++) { total += va_arg(ap, double); } va_end(ap); return total / count; } int message(const char * fmt, ...) { va_list ap; va_start(ap, fmt); int rc = vfprintf(stdout, fmt, ap); fputs("\n", stdout); va_end(ap); return rc; } int main( int argc, char ** argv ) { message("This is a message"); message("average: %lf", average(5, 25.0, 35.7, 50.1, 127.6, 75.0)); return 0; }

  • Defining Functions using recursion

    A recursive function is a function that calls itself. Lets make an example that calculate the factorial of a number. A factorial in math is typically defined in recursive terms. It is the product of n(n-1)(n-2), all the way down to 1.

    This actually called itself ten times. Now there is a limit to how many times you can do this, obviously there is a limit of the size of unsigned long integer, but there is also the limit of the amount of resources that are available on the computer. Every time you call a function, memory is allocated for the parameters, for any local variables, and for the return value and all the other function called overhead. If you do this recursively, it could happen many, many times, and all of those resources keep getting allocated and not deallocated until the last one is returned, and then all of those resources get deallocated, so this can be very inefficient.

    #include using namespace std; unsigned long int factorial( unsigned long int n ) { if( n < 2 ) return 1; return factorial( n - 1 ) * n; } int main( int argc, char ** argv ) { unsigned long int n = 10; cout

  • Defining Functions using recursion

    Regarding the previous issue of recursive functions, So if you have a choice between a recursive function and a function that works with a loop, oftentimes the loop will be a lot more efficient. For example, for the previous code, we could change like :

    #include using namespace std;

    unsigned long int factorial( unsigned long int n ) { if( n < 2 ) return 1; unsigned long int result = n ; while (n>1) result *= (--n); return result ; }

    int main( int argc, char ** argv ) { unsigned long int n = 10; cout