Functions#
(chapter 5)
Function Definition#
<Type> function_name(<parameter> list) /* header */
{
Declarations
/* body */
Statements
}
type hints in Python
def add(a: int, b: int) -> int:
The return statement#
double abs_value(double x)
{
if (x >= 0.0)
return x;
else
return -x;
}
The evaluated value is returned:
return;
return ++a;
return (a * b);
Function Prototypes#
double sqrt( double );
When declaring a function, the names of the input parameters do not matter, only their type and order.
void f(char c, int i); /* equivalent */
|
void f(char, int);
|
The structure of a program#
Preprocessor definitions/commands (start with #)
Function declarations (prototypes)
Function definitions (always main())
Example#
#include <stdio.h>
#define N 7
/* Function declaration (Prototypes) */
long power(int, int);
void prn_heading(void);
void prn_tbl_of_powers(int);
int main(void)
{
/* Using the functions */
prn_heading();
prn_tbl_of_powers(N);
return 0;
}
# @title
%%writefile power-table.c
#include <stdio.h>
#define N 7
/* Function declaration (Prototypes) */
long power(int base, int exp);
void prn_heading(void);
void prn_tbl_of_powers(int n);
int main(void)
{
/* Using the functions */
prn_heading();
prn_tbl_of_powers(N);
return 0;
}
/* Function to calculate power */
long power(int base, int exp)
{
long result = 1;
for (int i = 0; i < exp; i++)
{
result *= base;
}
return result;
}
/* Function to print heading */
void prn_heading(void)
{
printf("Table of Powers\n");
printf("================\n");
}
/* Function to print table of powers */
void prn_tbl_of_powers(int n)
{
for (int i = 1; i <= n; i++)
{
printf("%d^%d = %ld\n", i, i, power(i, i));
}
}
Cell In[1], line 6
*(Function, declaration, (Prototypes), */)
^
SyntaxError: invalid syntax
# @title
!gcc power-table.c -o power-table
!./power-table
Call by Value#
Summing integers from 1 to n
#include <stdio.h>
int compute_sum(int); /* Function declaration */
int main(void)
{
int n = 3, sum = 0;
printf("% d\n", n); /* 3 is printed */
sum = compute_sum(n); /* what happens to n and sum here? */
printf("% d\n", n); /* 3 is printed */
printf("% d\n", sum); /* 6 is printed */
return 0;
}
# @title
%%writefile call-by-value.c
# include <stdio.h>
int compute_sum(int); /* Function declaration */
int main(void)
{
int n = 3, sum = 0;
printf("% d\n", n); /* 3 is printed */
sum = compute_sum(n); /* what happens to n and sum here? */
printf("% d\n", n); /* 3 is printed */
printf("% d\n", sum); /* 6 is printed */
return 0;
}
/* sum the integers from 1 to n */
int compute_sum(int n)
{ /* This n is local. It gets the value of the of the variable that was sent to the function. */
int sum = 0; /* sum is local to the function. */
for (; n > 0; --n)
sum += n; /* local n and sum are changed */
return sum;
}
# @title
!gcc call-by-value.c -o call-by-value
!./call-by-value
Function invocation - summary#
Each expression in the argument list is evaluated.
The value of the expression is converted, if necessary, to the type of the formal parameter, and is assigned to its corresponding formal parameter (“call by value”) at the beginning of the function’s body.
The body of the function is executed.
If a ‘return’ statement is executed, then control is passed back to the calling environment.
If the ‘return’ statement includes an expression then the value of the expression is converted, if necessary, to the type given by the type specifier of the function, and that value is passed to the calling environment as well.
If the ‘return’ statement does not include an expression, no useful value is returned.
If no ‘return’ statement is present, control is passed back to the calling environment at the end of the function’s body. No useful value is returned.
Activation records#
Every instance of a function execution creates an activation record Activation records hold required execution information:
Local data (e.g., variables)
The state of the machine just before the call
A control link pointing to the activation record of the caller function
Supplied parameters
Return value of the function
Runtime Stack#
Activation records are created and stored in an area of memory termed the runtime stack.
void f2(char c, int i){
...
}
void f1(int i){
...
f2('a', i);
...
}
int main(void){
f1(1);
return 0;
}
|
+------------------+
| f2 | <- Stack Pointer (SP)
|------------------|
| return value |
| actual parameters|
| control link |
| machine status |
| local data |
+------------------+
| f1 |
|------------------|
| return value |
| actual parameters|
| control link |
| machine status |
| local data |
+------------------+
| Main |
|------------------|
| return value |
| actual parameters|
| control link |
| machine status |
| local data |
+------------------+
|
Storage Organization#
The program stack grows each time a function call is made.
Infinite recursion results in a collision between the runtime stack and the heap termed a run-time stack overflow error.
Recursion#
int fib(int num)
{
switch (num)
{
case 0:
return (0);
break;
case 1:
return (1);
break;
default:
return (fib(num - 1) + fib(num - 2));
break;
}
}
# @title
%%writefile fib_rec.c
#include <stdio.h>
/* Function prototype */
int fib(int num);
int main(void)
{
int num = 40; // Example input
printf("Fibonacci of %d is %d\n", num, fib(num));
return 0;
}
int fib(int num)
{
switch (num)
{
case 0:
return (0);
case 1:
return (1);
default:
return (fib(num - 1) + fib(num - 2));
}
}
# @title
!gcc fib_rec.c -o fib_rec
!./fib_rec
Iterative (non recursive) Solution#
int fib(int num)
{
unsigned int i = 0, fn = 0, fn_1 = 1, fn_2 = 0;
if (num == 0)
fn = 0;
else if (num == 1)
fn = 1;
else
{
for (i = 2; i <= num; ++i)
{
fn = fn_2 + fn_1;
fn_2 = fn_1;
fn_1 = fn;
}
}
return fn;
}
# @title
%%writefile fib_iter.c
#include <stdio.h>
/* Function prototype */
int fib(int num);
int main(void)
{
int num = 40; // Example input
printf("Fibonacci of %d is %d\n", num, fib(num));
return 0;
}
int fib(int num)
{
unsigned int i = 0, fn = 0, fn_1 = 1, fn_2 = 0;
if (num == 0)
fn = 0;
else if (num == 1)
fn = 1;
else
{
for (i = 2; i <= num; ++i)
{
fn = fn_2 + fn_1;
fn_2 = fn_1;
fn_1 = fn;
}
}
return fn;
}
# @title
!gcc fib_iter.c -o fib_iter
!./fib_iter
Recursive vs. iterative#
Function calls vs. loops
Recursion uses the runtime stack, less efficient
Recursion produces simpler and more intuitive code
Storage Classes#
Auto (local, not initialized)
Extern (global, keyword used only for variable declaration)
Static (private)
Scope Rules (auto)#
What is the output of this code?
{
int a = 1, b = 2, c = 3;
printf("% 3d % 3d % 3d\n", a, b, c);
{
int b = 4;
float c = 5.0;
printf("% 3d % 3d % 5.1f\n", a, b, c);
a = b;
{
int c;
c = b;
printf("% 3d % 3d % 3d\n", a, b, c);
}
printf("% 3d % 3d % 5.1f\n", a, b, c);
}
printf("% 3d % 3d % 3d\n", a, b, c);
}
# @title
%%writefile scope.c
#include <stdio.h>
int main(void)
{
int a = 1, b = 2, c = 3;
printf("%3d %3d %3d\n", a, b, c);
{
int b = 4;
float c = 5.0;
printf("%3d %3d %5.1f\n", a, b, c);
a = b;
{
int c;
c = b;
printf("%3d %3d %3d\n", a, b, c);
}
printf("%3d %3d %5.1f\n", a, b, c);
}
printf("%3d %3d %3d\n", a, b, c);
return 0;
}
# @title
!gcc scope.c -o scope
!./scope
Extern/Global#
#include <stdio.h>
int a = 1, b = 2, c = 3; // Global variables
int f(void); // Function prototype for f, which is in another file
int main(void) {
printf( "%3d\n", f() ); // Call to f(), prints its return value
printf( "%3d%3d%3d\n", a, b, c ); // Prints the values of a, b, and c
return 0;
}
Explanation:#
Global variables
a,b, andcare declared and initialized to 1, 2, and 3 respectively. These are visible across the entire program, including in other files.The
main()function callsf(), a function defined externally (in another file,file2.c), and prints its return value. It also prints the values ofa,b, andc.
External cont.#
int f( void ) {
extern int a; // Tells the compiler that 'a' is declared elsewhere (file1.c)
int b = 0, c = 0; // Local variables to the function 'f'
a = b = c = 4; // Modifies 'a', 'b', and 'c'
return (a + b + c); // Returns sum of a, b, and c
}
Explanation:#
extern int a: This tells the compiler thatais declared elsewhere, in this case infile1.c. This allowsf()to access and modify the global variablea.Local variables
bandcare declared and initialized insidef(); they only exist within the scope of the functionf().In the line
a = b = c = 4;, all three variables (a,b, andc) are assigned the value4. This modifies the global variableaand the local variablesbandc.The function returns the sum of
a,b, andc(which are all4).
# @title
%%writefile file1.c
# include <stdio.h>
int a = 1, b = 2, c = 3; // Global variables
int f(void); // Function prototype for f, which is in another file
int main(void) {
printf( "%3d\n", f() ); // Call to f(), prints its return value
printf( "%3d%3d%3d\n", a, b, c ); // Prints the values of a, b, and c
return 0;
}
# @title
%%writefile file2.c
int f( void ) {
extern int a; // Tells the compiler that 'a' is declared elsewhere (file1.c)
int b = 0, c = 0; // Local variables to the function 'f'
a = b = c = 4; // Modifies 'a', 'b', and 'c'
return (a + b + c); // Returns sum of a, b, and c
}
# @title
!gcc file1.c file2.c -o file1
!./file1
static#
void count_calls()
{
static int count = 0; // Static variable, retains value across calls
count++; // Increment count each time function is called
printf("Function has been called %d times\n", count);
}
# @title
%%writefile static.c
#include <stdio.h>
void count_calls()
{
static int count = 0; // Static variable, retains value across calls
count++; // Increment count each time function is called
printf("Function has been called %d times\n", count);
}
int main()
{
count_calls(); // 1st call
count_calls(); // 2nd call
count_calls(); // 3rd call
return 0;
}
# @title
!gcc static.c -o static
!./static
static external#
static int g( void ); /* function prototype */
void f( int a ) /* function definition */
{
g() /* g() is avaiable here but not in other files */
......
}
static int g( void ) /* function definition */
{
......
}
Explaination#
+--------------+ +--------------+
| file1.c | | file2.c |
+--------------+ +--------------+
| Calls f(5); | ----> | f() calls g()|
| Cannot call g()! | |
+--------------+ +--------------+
| g() is only visible here
+--------------+
Example Question#
Mathematical Functions#
The functions are declared in <math.h>
All the functions use doubles
Compile with
-lmflag
Example:
sqrt()pow()exp()log()sin()cos()tan()
# @title
%%writefile tmp.c
#include <stdio.h>
#include <math.h>
int main()
{
double num = 9.0;
// Square root
double square_root = sqrt(num);
printf("Square root of %.2f is %.2f\n", num, square_root);
return 0;
}
# @title
!gcc -o tmp tmp.c -lm
!./tmp
Assertions#
an assertion: used to enforce certain logical conditions. If conditions are not satisfied, the program terminates immediately.
#include <assert.h>
#include <stdio.h>
int f( int a, int b );
int main( void )
{
int a = 0, b = 0, c = 0;
....
scanf( “%d%d”, &a, &b );
c = f( a,b );
assert( c > 0 );
.....
}
Disabling Assertions#
In the code
#define NDEBUG
#include <assert.h>
During compilation
gcc -DNDEBUG program.c
# @title
%%writefile assert.c
#include <assert.h>
#include <stdio.h>
int f(int a, int b);
int main(void)
{
int a = 0, b = 0, c = 0;
// Call function f with a and b as parameters
c = f(a, b);
// Assert that the result of f is greater than 0
assert(c > 0); // If c <= 0, the program terminates here
printf("Function returned a positive value: %d\n", c);
return 5;
}
int f(int a, int b)
{
// Dummy function that returns a - b
return a - b;
}
# @title
!gcc -o assert assert.c
!./assert
assert vs if with return value#
// Using if with return
int function() {
if (pointer == NULL) {
return -1; // Error needs to be handled
}
// continue...
}
// Using assert
void function() {
assert(pointer != NULL); // Program stops if false
// continue...
}
Feature |
assert |
if return |
|---|---|---|
Purpose |
Debug/Development |
Production code |
When issue found |
Stops program immediately |
Returns error code |
Compile control |
Disabled with NDEBUG |
Always active |
Error handling |
No need for error checking |
Must handle errors |
Performance |
No overhead in release |
Always evaluated |
Usage |
Catch programming errors |
Handle runtime errors |
exit in C#
Purpose: Terminates the entire program immediately, regardless of where it is called.
Scope: Global; bypasses the function call stack.
Execution: Performs cleanup (e.g., flushing output buffers, closing files) before terminating the program.
#include <stdio.h>
#include <stdlib.h> // Required for exit()
void example_function() {
printf("Inside example_function\n");
exit(1); // Terminates the program
printf("This will not print.\n");
}
int main() {
printf("Program started.\n");
example_function();
printf("This will not print.\n");
return 0;
}