Unit 3 - Notes

CSE101

Unit 3: User defined functions and Storage classes

1. Introduction to Functions

A function is a self-contained block of statements that perform a specific task. C programming follows a modular approach (Divide and Conquer), where a large program is divided into smaller, manageable sub-programs called functions.

Advantages of using functions:

  • Reusability: Write code once and use it multiple times.
  • Maintainability: Easier to debug and isolate errors.
  • Abstraction: The caller needs to know what the function does, not how it does it.

2. Components of a Function

To use a function in C, three distinct steps are required:

A. Function Prototype (Declaration)

A prototype tells the compiler about the function's name, return type, and parameters before the function is actually defined. It ensures type checking.

Syntax:

C
return_type function_name(type1 argument1, type2 argument2, ...);

Example:
C
int add(int a, int b); // Declaration

B. Function Definition

This is the actual code block where the logic is implemented. It consists of the function header and the function body.

Syntax:

C
return_type function_name(type1 arg1, type2 arg2) {
    // Local variable declaration
    // Executable statements
    return value; // (Optional if return_type is void)
}

C. Function Call

Invoking the function to execute it. Control transfers from the calling function (e.g., main) to the called function.

Syntax:

C
function_name(value1, value2);


3. Parameter Passing Mechanisms

In C, arguments can be passed from the calling function to the called function in two ways.

A. Call by Value

In this method, the value of the actual parameters is copied into the formal parameters.

  • Key Characteristic: Changes made to the formal parameters inside the function do not affect the actual parameters in the calling function.
  • Memory: Distinct memory locations are allocated for actual and formal parameters.

Example (Swapping fails):

C
void swap(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
    // x and y are swapped here, but main() variables remain unchanged.
}

int main() {
    int a = 10, b = 20;
    swap(a, b); 
    // a is still 10, b is still 20
}

B. Call by Reference (Using Pointers)

In this method, the address (memory location) of the actual parameters is passed to the function. Formal parameters must be pointers.

  • Key Characteristic: Changes made inside the function directly affect the actual parameters in the calling function.
  • Memory: The function accesses the original memory location of the variables.

Example (Swapping succeeds):

C
void swap(int *x, int *y) {
    int temp = *x; // Get value at address x
    *x = *y;       // Put value at address y into address x
    *y = temp;     // Put temp into address y
}

int main() {
    int a = 10, b = 20;
    swap(&a, &b); // Pass addresses
    // Now a is 20, b is 10
}


4. Math Library Functions

C provides a standard library <math.h> meant for mathematical operations. Most functions in this library take double as arguments and return double.

Common Functions:

Function Description Example Input Output
ceil(x) Rounds up to the nearest integer. ceil(3.2) 4.0
floor(x) Rounds down to the nearest integer. floor(3.9) 3.0
sqrt(x) Calculates the square root. sqrt(16.0) 4.0
pow(x, y) Calculates raised to the power . pow(2, 3) 8.0
abs(x) Returns absolute value (integer). (Found in stdlib.h) abs(-5) 5
fabs(x) Returns absolute value (float). fabs(-5.5) 5.5
sin(x) Sine of x (x in radians). sin(0) 0.0

Usage Example:

C
#include <stdio.h>
#include <math.h>

int main() {
    double num = 25.0;
    printf("Square root of %.2f is %.2f", num, sqrt(num));
    return 0;
}

Note: When compiling on Linux/GCC, you may need to link the math library using -lm.


5. Recursive Functions

Recursion is a process where a function calls itself directly or indirectly.

Structure of Recursion:

  1. Base Case: The condition that stops the recursion. Without this, the program enters an infinite loop resulting in a Stack Overflow.
  2. Recursive Case: The part where the function calls itself with modified arguments to move closer to the base case.

Example: Factorial of a Number
Mathematical Logic:

C
int factorial(int n) {
    if (n == 0)      // Base Case
        return 1;
    else             // Recursive Case
        return n * factorial(n - 1);
}

Pros & Cons:

  • Pros: Code is cleaner and more compact for problems like tree traversals, Tower of Hanoi, or Fibonacci series.
  • Cons: Slower than iteration due to overhead of maintaining the function stack; consumes more memory.

6. Scope Rules

Scope refers to the region of the program where a variable is visible and accessible.

A. Local Scope

  • Variables declared inside a function or a block { ... }.
  • Accessibility: Only within that block/function.
  • Lifetime: Created when the block is entered, destroyed when exited.
  • Arguments passed to functions also have local scope.

B. Global Scope

  • Variables declared outside all functions (usually at the top of the program).
  • Accessibility: Visible to all functions in the file (from the point of declaration onwards).
  • Lifetime: Exists for the entire duration of the program execution.

Example:

C
int globalVar = 100; // Global Scope

void display() {
    int localVar = 5; // Local Scope
    printf("%d", globalVar); // Valid
    printf("%d", localVar);  // Valid
}

int main() {
    // printf("%d", localVar); // ERROR: localVar is not visible here
    return 0;
}


7. Storage Classes in C

Storage classes describe the features of a variable or function. They determine:

  1. Storage: Where the variable is stored (Stack, Register, Data Segment).
  2. Initial Value: What the default value is if not initialized.
  3. Scope: Where the variable can be accessed.
  4. Lifetime: How long the variable stays in memory.

There are four storage classes in C:

1. Automatic Storage Class (auto)

  • Keyword: auto (optional, as it is the default for local variables).
  • Storage: RAM (Stack).
  • Default Value: Garbage value.
  • Scope: Local to the block.
  • Lifetime: Until control remains within the block.

C
void function() {
    auto int a = 10; // Same as: int a = 10;
}

2. External Storage Class (extern)

  • Keyword: extern.
  • Storage: RAM (Data Segment).
  • Default Value: Zero.
  • Scope: Global (visible to all files in the program if linked properly).
  • Lifetime: Throughout the program execution.
  • Usage: Used to access a global variable defined in another file or to extend the visibility of a global variable.

C
extern int x; // Declaration (says x is defined elsewhere)

3. Static Storage Class (static)

This class preserves the value of a variable even after the variable's scope ends.

  • Keyword: static.
  • Storage: RAM (Data Segment).
  • Default Value: Zero.
  • Scope: Local to the block.
  • Lifetime: Throughout the program execution.

Behavior:
Unlike auto variables, static variables are initialized only once. They retain their value between multiple function calls.

Example:

C
void count() {
    static int c = 0; // Initialized only once
    c++;
    printf("%d ", c);
}

int main() {
    count(); // Output: 1
    count(); // Output: 2 (c remembered the previous value)
    count(); // Output: 3
}

4. Register Storage Class (register)

Instructs the compiler to store the variable in a CPU register instead of RAM for faster access.

  • Keyword: register.
  • Storage: CPU Register.
  • Default Value: Garbage value.
  • Scope: Local to the block.
  • Lifetime: Until control remains within the block.
  • Limitations:
    • We cannot obtain the address of a register variable using & (pointer).
    • If CPU registers are full, it defaults to auto.

C
register int i; // Used for loop counters for speed
for(i=0; i<1000; i++) { ... }

Summary Table of Storage Classes

Storage Class Keyword Storage Location Default Value Scope Lifetime
Automatic auto Stack Garbage Local Within block
Register register CPU Register Garbage Local Within block
Static static Data Segment Zero Local Entire Program
External extern Data Segment Zero Global Entire Program