======================== CBE Pt.9: Functions in C [TOC] ======================== ---- VIDEO ------------------------------ https://www.youtube.com/embed/VzugXD6X7sQ ----------------------------------------- Local Variables =============== ---- CODE (file=session11/c/local.c) ------------------------------------------- #include // for: int printf(const char *, ...); void foo(void) { int a = 42; int b = 123; printf("foo: a = %d, b = %d\n", a, b); } void bar(void) { int x; int y; printf("bar: x = %d, y = %d\n", x, y); } int main(void) { foo(); bar(); } -------------------------------------------------------------------------------- ---- SHELL (path=session11/c/) ------------------------------------------------- gcc -o local local.c ./local -------------------------------------------------------------------------------- Function Parameters: Call by Value ================================== Call by value means: If a function gets parameters it always gets a copy of them. In this example function `foo` changes its parameters. Because this is just a copy it has no effect to _original_ variable in function `main`: ---- CODE (file=session11/c/param.c) ------------------------------------------- #include // for: int printf(const char *, ...); void foo(int a) { a = 123; } int main(void) { int a = 42; printf("before calling foo(): a = %d\n", a); foo(a); printf("after calling foo(): a = %d\n", a); } -------------------------------------------------------------------------------- ---- SHELL (path=session11/c/) ------------------------------------------------- gcc -o param param.c param -------------------------------------------------------------------------------- How to Mimic "Call by Reference" ================================ Other programming languages, e.g. Fortran, use the _call by reference_ concept. If you pass a variable to a function as parameter you are giving away the _original_ variable. So if this function makes changes to it you would afterwards see the effect. If you want this behavior in C then you have to pass a pointer. The function will get a copy of this pointer but this copy still points to the same memory location. So this is how the above example can be changed to mimic the _call by reference_ in C: ---- CODE (file=session11/c/param2.c) ------------------------------------------ #include // for: int printf(const char *, ...); void foo(int *a) { *a = 123; } int main(void) { int a = 42; printf("before calling foo(): a = %d\n", a); foo(&a); printf("after calling foo(): a = %d\n", a); } -------------------------------------------------------------------------------- ---- SHELL (path=session11/c/) ------------------------------------------------- gcc -o param2 param2.c param2 -------------------------------------------------------------------------------- Recursive Function Calls ======================== The following program computes the factorial recursively. That means function `factorial` calls in some cases itself, but with a different parameter. Now first have a look at the code. Below you will find a graphic showing the stach frames when `factorial` gets called with `n` equal to 3. ---- CODE (file=session11/c/factorial.c) --------------------------------------- #include // for: int printf(const char *, ...); unsigned long long factorial(unsigned long long n) { return n <= 1 ? 1 : n * factorial(n - 1); } int main(void) { // 20! can be represented with 64 bits but 21! can not. for (unsigned long long n = 0; n < 21; ++n) { printf("%2llu! = %llu\n", n, factorial(n)); } } -------------------------------------------------------------------------------- Assume we are in function main _before_ a call of `factorial(3)` takes place. And the stack looks like that (everything on the right-hand side of the stack pointer is somehow in use): ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!40]{24}{33}{used} \DrawMemLabel{24}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- Right after the call of `factorial(3)` we therefore have a stack frame for this call instance: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!20]{16}{24}{stack frame for factorial(3)} \DrawMemVariable[gray!40]{24}{33}{used} \DrawMemLabel{16}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- Because `n <= 1` is in this instance false there will be a call of `factorial(2)`. Right after this call we have another stack frame. This time for the function call instance `factorial(2)`: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!20]{8}{16}{stack frame for factorial(2)} \DrawMemVariable[gray!20]{16}{24}{stack frame for factorial(3)} \DrawMemVariable[gray!40]{24}{33}{used} \DrawMemLabel{8}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- In this instance `n <= 1` is also false. So there will be another call, hence another instance, this time for `factorial(1)`: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!20]{0}{8}{stack frame for factorial(1)} \DrawMemVariable[gray!20]{8}{16}{stack frame for factorial(2)} \DrawMemVariable[gray!20]{16}{24}{stack frame for factorial(3)} \DrawMemVariable[gray!40]{24}{33}{used} \DrawMemLabel{0}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- In this instance `n <= 1` is true. So it will return 1 and the stack frame gets "cleared": ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!20]{8}{16}{stack frame for factorial(2)} \DrawMemVariable[gray!20]{16}{24}{stack frame for factorial(3)} \DrawMemVariable[gray!40]{24}{33}{used} \DrawMemLabel{8}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- So `factorial(2)` now return 2 (= `2 * factorial(1)`) and right after that we have ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!20]{16}{24}{stack frame for factorial(3)} \DrawMemVariable[gray!40]{24}{33}{used} \DrawMemLabel{16}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- So `factorial(3)` will return 6 (= `3 * factorial(2)`) and we have the stack pointer at the same position as when we started: ---- TIKZ ---------------------------------------------------------------------- \begin{tikzpicture} \input{memory.tex} \renewcommand\MemCellWidth {0.7} \DrawMemArrayOpenLeft{-1}{32} \DrawMemVariable[red!40]{24}{33}{used} \DrawMemLabel{24}{Stack Pointer} \end{tikzpicture} -------------------------------------------------------------------------------- ---- SHELL (path=session11/c/) ------------------------------------------------- gcc -o factorial factorial.c factorial --------------------------------------------------------------------------------