โ† All units

๐Ÿงฉ Unit 4 ยท Functions & Pointers

Helpers that do jobs for you, and papers that remember where things live

1. Modular programming

Mom is planning a birthday party. She doesn't do every job alone. One helper decorates, one bakes the cake, one sings. Mom just calls the right helper at the right time.

A function is a helper with a name that does one job. main() is the boss that calls the helpers. Breaking a big program into small functions is called modular programming (each function is a "module").

โ™ป๏ธ Reuse
Write once, call many times.
๐Ÿ” Easy to find bugs
A small helper is easy to check.
๐Ÿ‘ฅ Teamwork
Different people write different functions.
๐Ÿ“– Easy to read
bakeCake(); tells you what happens.
Modular programming = dividing a program into independent functions (modules). Advantages: reusability, easy debugging, readability, teamwork, less code repetition. Draw main() at top with arrows to each function.

File: 01_modular_programming.c

/*
 * Program 1: Modular Programming
 * ------------------------------
 * Real-life story: Making a birthday party.
 * Mom does not do everything alone. She gives jobs to helpers:
 *   - one helper decorates
 *   - one helper bakes the cake
 *   - one helper sings the song
 * Each helper = one FUNCTION. main() is Mom, who calls the helpers.
 *
 * Breaking a big program into small functions is called MODULAR PROGRAMMING.
 */
#include <stdio.h>

void decorate() {                  /* helper 1 */
    printf("Helper 1: Balloons are up!\n");
}

void bakeCake() {                  /* helper 2 */
    printf("Helper 2: Cake is ready!\n");
}

void singSong() {                  /* helper 3 */
    printf("Helper 3: Happy birthday to you!\n");
}

int main() {                       /* Mom, the boss */
    printf("Party planning starts...\n");
    decorate();                    /* call helper 1 */
    bakeCake();                    /* call helper 2 */
    singSong();                    /* call helper 3 */
    singSong();                    /* we can call a helper again and again! */
    printf("Party done!\n");
    return 0;
}

Output

Party planning starts...
Helper 1: Balloons are up!
Helper 2: Cake is ready!
Helper 3: Happy birthday to you!
Helper 3: Happy birthday to you!
Party done!

2. Function prototype, definition and call

At a juice shop: the menu board says "Juice: give 2 fruits, get 1 juice". The kitchen is where it's really made. You ordering is the call.
PartJuice shopExampleWhere
Prototype (declaration)Menu boardint addFruits(int a, int o);Above main, ends with ;
DefinitionKitchenint addFruits(int a, int o) { return a + o; }Anywhere, has a body { }
CallOrderingtotal = addFruits(3, 2);Inside another function
return_type  function_name ( parameter list )
    int        addFruits    ( int apples, int oranges )
     โ”‚             โ”‚                 โ”‚
     โ”‚             โ”‚                 โ””โ”€ what goes IN (formal parameters)
     โ”‚             โ””โ”€ the helper's name
     โ””โ”€ what comes OUT (void = nothing)
Actual arguments are the real values you send in the call (3, 2). Formal parameters are the names that receive them inside the function (apples, oranges).
If you call a function before the compiler has seen it, and there is no prototype, you get an "implicit declaration" error or warning. Add the prototype on top.
๐Ÿงƒ Try it: be the function
apples: oranges:

  
Prototype tells the compiler the return type, name, and parameter types before use. Definition = header + body. Call = name with actual arguments. return sends one value back and ends the function.

File: 02_prototype_definition_call.c

/*
 * Program 2: Function Prototype, Definition and Call
 * --------------------------------------------------
 * Real-life story: A juice shop.
 *   PROTOTYPE  = the menu board: "Juice: give 2 fruits, get 1 juice"
 *                (tells the name, what goes in, what comes out)
 *   DEFINITION = the kitchen: how the juice is actually made
 *   CALL       = you ordering: "One juice with 3 apples and 2 oranges, please!"
 */
#include <stdio.h>

/* 1. PROTOTYPE (declaration): return type, name, parameter types, then ; */
int addFruits(int apples, int oranges);

int main() {
    int total;

    /* 3. CALL: we send 3 and 2 (actual arguments) */
    total = addFruits(3, 2);
    printf("Total fruits in the juice = %d\n", total);

    /* call again with different values */
    printf("Another juice uses %d fruits\n", addFruits(5, 4));
    return 0;
}

/* 2. DEFINITION: the real work. apples and oranges are formal parameters */
int addFruits(int apples, int oranges) {
    int sum = apples + oranges;
    return sum;                    /* send the answer back to whoever called */
}

Output

Total fruits in the juice = 5
Another juice uses 9 fruits

3. The 4 types of functions

Talking to friends: one just says "Hi". One shouts your roll number back. One tells you their age. One adds two numbers you give and tells you the answer.
#TypeGive something?Get something back?Prototype
1No arguments, no returnโŒโŒvoid sayHi(void);
2With arguments, no returnโœ…โŒvoid greet(int rollNo);
3No arguments, with returnโŒโœ…int getAge(void);
4With arguments, with returnโœ…โœ…int multiply(int a, int b);
This is a very common 16-mark question. Write all 4 with a small example each and say: "void" return type means no value returned; empty/void parameter list means no arguments.

File: 03_function_categories.c

/*
 * Program 3: The 4 kinds of functions
 * -----------------------------------
 * Real-life story: Talking to friends.
 *  1. No arguments, no return   : friend just says "Hi!"           (you give nothing, get nothing)
 *  2. Arguments, no return      : you give a name, friend shouts it (you give, get nothing back)
 *  3. No arguments, with return : friend tells you their age        (you give nothing, get a value)
 *  4. Arguments, with return    : you give 2 numbers, friend adds   (you give, and get a value)
 */
#include <stdio.h>

void sayHi(void);                  /* type 1 */
void greet(int rollNo);            /* type 2 */
int getAge(void);                  /* type 3 */
int multiply(int a, int b);        /* type 4 */

int main() {
    sayHi();                                   /* 1 */
    greet(25);                                 /* 2 */
    int age = getAge();                        /* 3 */
    printf("Friend's age is %d\n", age);
    int ans = multiply(6, 7);                  /* 4 */
    printf("6 x 7 = %d\n", ans);
    return 0;
}

void sayHi(void) {
    printf("Type 1: Hi!\n");
}

void greet(int rollNo) {
    printf("Type 2: Hello roll number %d!\n", rollNo);
}

int getAge(void) {
    printf("Type 3: ");
    return 12;
}

int multiply(int a, int b) {
    printf("Type 4: ");
    return a * b;
}

Output

Type 1: Hi!
Type 2: Hello roll number 25!
Type 3: Friend's age is 12
Type 4: 6 x 7 = 42

4. Built-in string functions

You're making name stickers for school books. C already gives you a toolbox: count letters, photocopy, glue two names together, compare, make capitals.
FunctionHeaderDoesExample โ†’ result
strlen(s)string.hCounts letters (not '\0')strlen("Ravi") โ†’ 4
strcpy(a, b)string.hCopies b into aa becomes "Ravi"
strcat(a, b)string.hGlues b to the end of a"Ravi" + " Kumar" โ†’ "Ravi Kumar"
strcmp(a, b)string.h0 if same, negative if a comes first, positive if b comes firststrcmp("cat","dog") โ†’ negative
toupper(c) / tolower(c)ctype.hChange one letter's casetoupper('r') โ†’ 'R'
strrev(s)not standardReverse (only some old compilers have it)Write your own loop instead
For strcpy and strcat, the target array must be big enough. char first[30] has room; char first[] = "Ravi" only has room for 5 characters.
๐Ÿท๏ธ Try the toolbox
a: b:

  
List each function with syntax, purpose and an example. Mention #include <string.h>. For strcmp, say it compares character by character using ASCII values.

File: 04_string_functions.c

/*
 * Program 4: Built-in STRING functions  (#include <string.h>, <ctype.h>)
 * ----------------------------------------------------------------------
 * Real-life story: Name stickers for school books.
 * C gives us ready-made helpers so we don't write everything ourselves.
 *
 *   strlen(s)       -> how many letters
 *   strcpy(a, b)    -> copy b into a          (photocopy)
 *   strcat(a, b)    -> stick b at end of a    (glue)
 *   strcmp(a, b)    -> 0 if same, <0 or >0 if different
 *   toupper(ch)     -> make one letter CAPITAL
 *   strrev is NOT standard C, so we reverse with our own loop.
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main() {
    char first[30] = "Ravi";
    char last[] = "Kumar";
    char copy[30];
    int i, len;

    /* strlen */
    printf("strlen(\"%s\") = %lu letters\n", first, (unsigned long)strlen(first));

    /* strcpy: photocopy first into copy */
    strcpy(copy, first);
    printf("strcpy -> copy now has \"%s\"\n", copy);

    /* strcat: glue a space and the last name to first */
    strcat(first, " ");
    strcat(first, last);
    printf("strcat -> full name \"%s\"\n", first);

    /* strcmp: compare words */
    printf("strcmp(\"cat\", \"cat\") = %d  (same)\n", strcmp("cat", "cat"));
    printf("strcmp(\"cat\", \"dog\") is %s  (c comes before d)\n",
           strcmp("cat", "dog") < 0 ? "negative" : "positive");

    /* toupper: one letter at a time */
    for (i = 0; first[i] != '\0'; i++) {
        first[i] = toupper(first[i]);
    }
    printf("toupper -> \"%s\"\n", first);

    /* reverse (our own strrev) : swap first and last letters, move inward */
    len = strlen(copy);
    for (i = 0; i < len / 2; i++) {
        char temp = copy[i];
        copy[i] = copy[len - 1 - i];
        copy[len - 1 - i] = temp;
    }
    printf("reverse -> \"%s\"\n", copy);

    return 0;
}

Output

strlen("Ravi") = 4 letters
strcpy -> copy now has "Ravi"
strcat -> full name "Ravi Kumar"
strcmp("cat", "cat") = 0  (same)
strcmp("cat", "dog") is negative  (c comes before d)
toupper -> "RAVI KUMAR"
reverse -> "ivaR"

5. Built-in math functions

In the garden: a square patch has area 49, so how long is one side? 130 students and each bus holds 40, so how many buses? Math functions answer these.
FunctionDoesExample
sqrt(x)Square rootsqrt(49) = 7
pow(x, y)x to the power ypow(2, 3) = 8
abs(x)Remove minus sign (int, in stdlib.h)abs(-5) = 5
fabs(x)Same for decimalsfabs(-2.5) = 2.5
ceil(x)Round UPceil(4.2) = 5
floor(x)Round DOWNfloor(9.8) = 9
On Linux, math programs need -lm at the end: gcc 05_math_functions.c -o program -lm. It tells gcc to link the math library. On Mac it works without it, but adding it does no harm.
๐Ÿงฎ Try it
x: y:

  
Include <math.h>, functions return double. Know the difference between ceil and floor with an example.

File: 05_math_functions.c

/*
 * Program 5: Built-in MATH functions  (#include <math.h>)
 * -------------------------------------------------------
 * Real-life story: A garden.
 *   sqrt  -> a square garden has area 49. How long is one side?  sqrt(49) = 7
 *   pow   -> 3 rows, each row doubles... 2 to the power 3 = 8
 *   abs   -> distance is never negative: abs(-5) = 5
 *   ceil  -> 4.2 buses needed? You must book 5 buses (round UP)
 *   floor -> Rs 9.8 in pocket, how many Rs 1 toffees? 9 (round DOWN)
 *
 * IMPORTANT: compile with -lm to link the math library:
 *     gcc 05_math_functions.c -o program -lm
 */
#include <stdio.h>
#include <math.h>
#include <stdlib.h>     /* abs() for int lives here */

int main() {
    printf("sqrt(49)    = %.1f\n", sqrt(49));
    printf("pow(2, 3)   = %.1f\n", pow(2, 3));
    printf("abs(-5)     = %d\n", abs(-5));
    printf("fabs(-2.5)  = %.1f\n", fabs(-2.5));
    printf("ceil(4.2)   = %.1f\n", ceil(4.2));
    printf("floor(9.8)  = %.1f\n", floor(9.8));

    /* real use: 130 students, a bus holds 40. How many buses? */
    int students = 130, busSize = 40;
    printf("\nBuses needed for %d students = %.0f\n",
           students, ceil((double)students / busSize));
    return 0;
}

Output

sqrt(49)    = 7.0
pow(2, 3)   = 8.0
abs(-5)     = 5
fabs(-2.5)  = 2.5
ceil(4.2)   = 5.0
floor(9.8)  = 9.0

Buses needed for 130 students = 4

6. Recursion: a function calling itself

Russian dolls: you open a doll, and there's a smaller doll inside. You keep opening until you reach the tiniest one that doesn't open. Then you put them all back together.

Recursion means a function calls itself with a smaller problem. Every recursive function needs two things:

  1. Base case: when to STOP (the tiniest doll). Without it, the program crashes ("stack overflow").
  2. Recursive case: call yourself with a smaller value.
fact(5) = 5 * fact(4)
                 fact(4) = 4 * fact(3)
                              fact(3) = 3 * fact(2)
                                           fact(2) = 2 * fact(1)
                                                        fact(1) = 1   โ† base case
Going back up:   2*1=2 โ†’ 3*2=6 โ†’ 4*6=24 โ†’ 5*24=120
๐Ÿ“š Call-stack stepper: each call is a plate placed on a pile. The top plate is the one running now.
n =
RecursionLoop (iteration)
Function calls itselfUses for/while
Short, neat codeCode can be longer
Uses more memory (stack)Uses less memory
Needs a base caseNeeds a stopping condition
Define recursion, state base case + recursive case, write factorial, and trace it for n = 5 like the picture above. Compare recursion vs iteration.

File: 06_recursion_factorial.c

/*
 * Program 6: Recursion - Factorial
 * --------------------------------
 * Real-life story: Russian dolls (a doll inside a doll inside a doll...).
 * You keep opening dolls until you reach the tiniest one. Then you stop.
 *
 * RECURSION = a function that calls ITSELF.
 * It needs:
 *   1. a BASE CASE (the tiniest doll) -> stop calling
 *   2. a smaller problem each time
 *
 *   5! = 5 x 4 x 3 x 2 x 1 = 120
 *   fact(5) = 5 * fact(4)
 *   fact(4) = 4 * fact(3) ... down to fact(1) = 1
 */
#include <stdio.h>

int fact(int n) {
    printf("  calling fact(%d)\n", n);
    if (n <= 1) {                  /* BASE CASE: tiniest doll */
        printf("  fact(%d) returns 1  (base case, start going back)\n", n);
        return 1;
    }
    int answer = n * fact(n - 1);  /* open the next smaller doll */
    printf("  fact(%d) returns %d\n", n, answer);
    return answer;
}

int main() {
    int n = 5;
    int result = fact(n);
    printf("\n%d! = %d\n", n, result);
    return 0;
}

Output

  calling fact(5)
  calling fact(4)
  calling fact(3)
  calling fact(2)
  calling fact(1)
  fact(1) returns 1  (base case, start going back)
  fact(2) returns 2
  fact(3) returns 6
  fact(4) returns 24
  fact(5) returns 120

5! = 120

7. Recursion: Fibonacci

Rabbits multiply! Each new number is the two before it added together: 0, 1, 1, 2, 3, 5, 8...
fib(n) = 0                      if n == 0
       = 1                      if n == 1
       = fib(n-1) + fib(n-2)    otherwise
              fib(4)
            /        \
        fib(3)       fib(2)
        /    \       /    \
    fib(2) fib(1) fib(1) fib(0)
    /    \
 fib(1) fib(0)
fib calls itself two times, so it does a lot of repeated work (fib(2) is calculated twice above). For big n, a loop is much faster.
๐Ÿ‡ How many calls?
n =

  
Write the recursive fib function with two base cases and draw the recursion tree for fib(4).

File: 07_recursion_fibonacci.c

/*
 * Program 7: Recursion - Fibonacci
 * --------------------------------
 * Real-life story: Rabbits! Every new number = the two numbers before it added.
 *   0, 1, 1, 2, 3, 5, 8, 13, ...
 *
 *   fib(0) = 0           (base case)
 *   fib(1) = 1           (base case)
 *   fib(n) = fib(n-1) + fib(n-2)
 */
#include <stdio.h>

int fib(int n) {
    if (n == 0) return 0;          /* base case 1 */
    if (n == 1) return 1;          /* base case 2 */
    return fib(n - 1) + fib(n - 2);/* call myself twice */
}

int main() {
    int i;
    printf("First 10 Fibonacci numbers:\n");
    for (i = 0; i < 10; i++) {
        printf("%d ", fib(i));
    }
    printf("\n");
    return 0;
}

Output

First 10 Fibonacci numbers:
0 1 1 2 3 5 8 13 21 34

8. Binary search using recursion

Finding a word in a dictionary: you open the middle. If your word comes before it, you only look in the left half. Each time, half the book disappears!
  1. Find mid = (low + high) / 2.
  2. If a[mid] == key โ†’ found! (base case)
  3. If key < a[mid] โ†’ search the left half: binarySearch(a, low, mid-1, key)
  4. Else โ†’ search the right half: binarySearch(a, mid+1, high, key)
  5. If low > high โ†’ not found (base case)
The array MUST be sorted for binary search.
๐Ÿ“– Watch the halves disappear
Find:
Binary search takes about logโ‚‚(n) steps. 1000 items need only about 10 checks. Linear search may need 1000.

File: 08_recursive_binary_search.c

/*
 * Program 8: Binary Search using Recursion
 * ----------------------------------------
 * Real-life story: Finding a word in a dictionary.
 * You don't read every page! You open the MIDDLE.
 *   - Word is before the middle? Look only in the LEFT half.
 *   - Word is after?             Look only in the RIGHT half.
 * Each time, half the book disappears. Very fast!
 *
 * RULE: the array must be SORTED first.
 */
#include <stdio.h>

int binarySearch(int a[], int low, int high, int key) {
    if (low > high) {              /* BASE CASE: nothing left to search */
        return -1;
    }
    int mid = (low + high) / 2;
    printf("  looking between index %d and %d, middle a[%d] = %d\n", low, high, mid, a[mid]);

    if (a[mid] == key) {           /* BASE CASE: found it */
        return mid;
    } else if (key < a[mid]) {     /* go LEFT */
        return binarySearch(a, low, mid - 1, key);
    } else {                       /* go RIGHT */
        return binarySearch(a, mid + 1, high, key);
    }
}

int main() {
    int marks[] = {10, 22, 35, 47, 58, 63, 79, 88, 94};   /* sorted */
    int n = 9;
    int key = 63;

    printf("Searching for %d:\n", key);
    int pos = binarySearch(marks, 0, n - 1, key);
    if (pos != -1) printf("Found %d at index %d\n\n", key, pos);
    else           printf("%d not found\n\n", key);

    key = 40;
    printf("Searching for %d:\n", key);
    pos = binarySearch(marks, 0, n - 1, key);
    if (pos != -1) printf("Found %d at index %d\n", key, pos);
    else           printf("%d not found\n", key);
    return 0;
}

Output

Searching for 63:
  looking between index 0 and 8, middle a[4] = 58
  looking between index 5 and 8, middle a[6] = 79
  looking between index 5 and 5, middle a[5] = 63
Found 63 at index 5

Searching for 40:
  looking between index 0 and 8, middle a[4] = 58
  looking between index 0 and 3, middle a[1] = 22
  looking between index 2 and 3, middle a[2] = 35
  looking between index 3 and 3, middle a[3] = 47
40 not found

9. Pointers: & and *

Every house has an address, like "12, Rose Street". A pointer is a piece of paper with the address written on it. With the paper you can visit the house and even change what's inside.

Every variable lives in memory at an address. A pointer is a variable that stores an address.

WriteRead it asMeaning
int *p;"p is a pointer to int"Declare a pointer
&x"address of x"Address operator
p = &x;"p points to x"Store the address
*p"value at p"Dereference (indirection) operator: go to the address and look
*p = 25;"put 25 at p"Changes x!
๐Ÿ  Memory map (addresses are pretend)

      
Real addresses printed with %p (like 0x16f355fb8) change every time you run the program. The output below is from one run, and yours will be different. That's normal.
Never use *p before giving p an address. Such a "wild pointer" points to random memory. Use int *p = NULL; if you have nothing to point to yet.
Pointer = variable that stores the address of another variable. Declaration datatype *name;. Operators: & (address of), * (value at address). Advantages: pass by reference, dynamic memory, efficient arrays/strings, data structures like linked lists.

File: 09_pointer_basics.c

/*
 * Program 9: Pointers - & and *
 * -----------------------------
 * Real-life story: Your house and its address.
 *   - The house holds people (the VALUE).
 *   - The address tells WHERE the house is ("12, Rose Street").
 *   - A POINTER is a paper with the address written on it.
 *
 *   &x  -> "address of x"      (where does x live?)
 *   *p  -> "value at address p" (go to that house and look inside)
 *
 * NOTE: the addresses printed with %p change every time you run the program.
 */
#include <stdio.h>

int main() {
    int candies = 10;          /* a house holding 10 */
    int *p;                    /* p is a pointer: it can hold the address of an int */

    p = &candies;              /* write candies' address on the paper p */

    printf("candies           = %d\n", candies);
    printf("&candies (address)= %p\n", (void *)&candies);
    printf("p (same address)  = %p\n", (void *)p);
    printf("*p (value there)  = %d\n", *p);

    *p = 25;                   /* go to the address and change what's inside */
    printf("\nAfter *p = 25, candies = %d  (changed through the pointer!)\n", candies);

    /* pointer to pointer: a paper that has the address of another paper */
    int **pp = &p;
    printf("**pp              = %d\n", **pp);

    printf("\nSize of an int pointer on this computer = %lu bytes\n", (unsigned long)sizeof(p));
    return 0;
}

Output (addresses will differ on your computer)

candies           = 10
&candies (address)= 0x16ee89fe8
p (same address)  = 0x16ee89fe8
*p (value there)  = 10

After *p = 25, candies = 25  (changed through the pointer!)
**pp              = 25

Size of an int pointer on this computer = 8 bytes

10. Pointer arithmetic

Houses on a street are all the same width. "Go to the next house" means walking one house-width, not one footstep.

p + 1 moves to the next element, which is sizeof(type) bytes ahead.

OperationAllowed?Meaning
p++, p--โœ…Next / previous element
p + n, p - nโœ…Jump n elements
q - pโœ…How many elements apart (same array)
p < q, p == qโœ…Compare positions
p + q, p * 2, p / 2โŒMeaningless, so not allowed
๐Ÿšถ Walk the street (int = 4 bytes, start address 1000 is pretend)

  
If p is an int pointer at address 1000, then p+1 = 1004 (because sizeof(int) = 4). Pointer subtraction gives the number of elements between, not bytes.

File: 10_pointer_arithmetic.c

/*
 * Program 10: Pointer Arithmetic
 * ------------------------------
 * Real-life story: Houses on a street, all the same size.
 * If one house is 4 steps wide, then "next house" = 4 steps ahead.
 *
 * p + 1 does NOT add 1 byte. It jumps to the NEXT ELEMENT.
 *   int is 4 bytes   -> p + 1 moves 4 bytes
 *   char is 1 byte   -> p + 1 moves 1 byte
 *   double is 8 bytes-> p + 1 moves 8 bytes
 *
 * Allowed: p++, p--, p + n, p - n, p2 - p1 (gap between), compare p1 < p2
 * NOT allowed: p1 + p2, p * 2, p / 2
 */
#include <stdio.h>

int main() {
    int street[5] = {100, 200, 300, 400, 500};
    int *p = &street[0];          /* stand at house 0 */
    int *q = &street[4];          /* friend stands at house 4 */

    printf("*p        = %d  (house 0)\n", *p);
    p++;                           /* walk to next house */
    printf("after p++, *p = %d  (house 1)\n", *p);
    p = p + 2;                     /* jump 2 houses */
    printf("after p+2, *p = %d  (house 3)\n", *p);
    p--;                           /* step back */
    printf("after p--, *p = %d  (house 2)\n", *p);

    printf("\nGap q - p = %ld houses\n", (long)(q - p));
    printf("Is p before q? %s\n", (p < q) ? "yes" : "no");

    /* how many BYTES does +1 move? */
    char c[2];
    double d[2];
    printf("\nint    : +1 moves %ld bytes\n", (long)((char *)(street + 1) - (char *)street));
    printf("char   : +1 moves %ld byte\n",  (long)((char *)(c + 1) - (char *)c));
    printf("double : +1 moves %ld bytes\n", (long)((char *)(d + 1) - (char *)d));
    return 0;
}

Output

*p        = 100  (house 0)
after p++, *p = 200  (house 1)
after p+2, *p = 400  (house 3)
after p--, *p = 300  (house 2)

Gap q - p = 2 houses
Is p before q? yes

int    : +1 moves 4 bytes
char   : +1 moves 1 byte
double : +1 moves 8 bytes

11. Arrays and pointers

A train: the array name is the engine, and it always points to coach 0. To reach coach 3, start at the engine and go 3 coaches ahead.
These are the same
a  =  &a[0]
a[i]  =  *(a + i)  =  *(p + i)  =  p[i]
&a[i]  =  a + i
You can do p++ but NOT a++. The array name is a fixed (constant) address.
๐Ÿš‚ Pick a coach
i =

  
When an array is passed to a function, only the address of the first element is passed, so the function can change the original array.

File: 11_arrays_and_pointers.c

/*
 * Program 11: Arrays and Pointers
 * -------------------------------
 * Real-life story: A train with coaches.
 * The NAME of the array is like the engine: it points to coach 0.
 *
 *   a        == &a[0]       (array name = address of first element)
 *   a[i]     == *(a + i)    (coach i = go i steps from the engine and look inside)
 *   &a[i]    == a + i
 */
#include <stdio.h>

int main() {
    int a[5] = {5, 10, 15, 20, 25};
    int *p = a;                    /* same as p = &a[0] */
    int i, sum = 0;

    printf("i   a[i]   *(a+i)   *(p+i)   p[i]\n");
    for (i = 0; i < 5; i++) {
        printf("%d   %3d     %3d      %3d     %3d\n", i, a[i], *(a + i), *(p + i), p[i]);
    }

    /* walk the train with the pointer only */
    for (p = a; p < a + 5; p++) {
        sum = sum + *p;
    }
    printf("\nSum using a moving pointer = %d\n", sum);

    printf("a == &a[0]? %s\n", (a == &a[0]) ? "yes" : "no");
    return 0;
}

Output

i   a[i]   *(a+i)   *(p+i)   p[i]
0     5       5        5       5
1    10      10       10      10
2    15      15       15      15
3    20      20       20      20
4    25      25       25      25

Sum using a moving pointer = 75
a == &a[0]? yes

12. Array of pointers

The attendance list. Names are different lengths ("Anu" is short, "Chitra" is longer). Instead of giving every name a huge fixed box, keep a list of arrows, each pointing to a name.
names[0] โ”€โ”€โ–บ "Anu"
names[1] โ”€โ”€โ–บ "Bala"
names[2] โ”€โ”€โ–บ "Chitra"
names[3] โ”€โ”€โ–บ "Dev"
char names[4][10] (2D array)char *names[4] (array of pointers)
MemoryEvery row is 10 chars, even "Dev"Each name uses only what it needs
Swap two namesCopy all the lettersJust swap two arrows
Don't confuse int *p[3] (array of 3 pointers) with int (*p)[3] (one pointer to an array of 3 ints).
Array of pointers = an array whose every element is an address. Common use: a list of strings, which saves memory and makes sorting fast because only the pointers are swapped.

File: 12_array_of_pointers.c

/*
 * Program 12: Array of Pointers
 * -----------------------------
 * Real-life story: A class attendance list.
 * Names are different lengths. Instead of giving every name a big
 * fixed box, we keep a list of POINTERS, each pointing to one name.
 *
 *   char *names[4]  -> 4 pointers, each points to a string
 *   int  *ptr[3]    -> 3 pointers, each points to an int
 */
#include <stdio.h>

int main() {
    char *names[4] = {"Anu", "Bala", "Chitra", "Dev"};
    int i;

    printf("Attendance list:\n");
    for (i = 0; i < 4; i++) {
        printf("%d. %s  (first letter %c)\n", i + 1, names[i], *names[i]);
    }

    /* swapping two pointers swaps the order WITHOUT copying the names */
    char *temp = names[0];
    names[0] = names[3];
    names[3] = temp;
    printf("\nAfter swapping first and last pointer: %s ... %s\n", names[0], names[3]);

    /* array of int pointers */
    int x = 7, y = 8, z = 9;
    int *ptr[3] = {&x, &y, &z};
    printf("\nValues through int pointers: ");
    for (i = 0; i < 3; i++) {
        printf("%d ", *ptr[i]);
    }
    printf("\n");
    return 0;
}

Output

Attendance list:
1. Anu  (first letter A)
2. Bala  (first letter B)
3. Chitra  (first letter C)
4. Dev  (first letter D)

After swapping first and last pointer: Dev ... Anu

Values through int pointers: 7 8 9

13. Pass by value vs pass by reference

Your homework notebook. Pass by value: you give your friend a photocopy. They scribble on it, and your notebook is safe. Pass by reference: you give your friend your home address. They come and write in your real notebook, so it changes!
๐Ÿ”„ Swap demo
x = y =
main()
function

  
Pass by valuePass by reference
What is sentA copy of the valueThe address (&x)
Parameterint aint *a
Changes original?NoYes
Example callswap(x, y)swap(&x, &y)
Classic 16-mark question: write both swap functions, show output, and explain that in call by value the formal parameters are copies, while in call by reference the function works on the original memory through pointers.

File: 13_pass_by_value_reference.c

/*
 * Program 13: Pass by Value vs Pass by Reference
 * ----------------------------------------------
 * Real-life story: Your homework notebook.
 *   PASS BY VALUE     = you give your friend a PHOTOCOPY.
 *                       Friend scribbles on it -> your notebook is safe.
 *   PASS BY REFERENCE = you give your friend your HOME ADDRESS.
 *                       Friend comes and writes in your real notebook -> it changes!
 *
 * In C, "pass by reference" is done by passing ADDRESSES (pointers).
 */
#include <stdio.h>

void swapByValue(int a, int b) {       /* gets photocopies */
    int temp = a;
    a = b;
    b = temp;
    printf("  inside swapByValue:     a = %d, b = %d\n", a, b);
}

void swapByReference(int *a, int *b) { /* gets addresses */
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("  inside swapByReference: *a = %d, *b = %d\n", *a, *b);
}

int main() {
    int x = 10, y = 20;

    printf("Start:                   x = %d, y = %d\n", x, y);

    swapByValue(x, y);
    printf("After swapByValue:       x = %d, y = %d   (NOT swapped)\n\n", x, y);

    swapByReference(&x, &y);
    printf("After swapByReference:   x = %d, y = %d   (swapped!)\n", x, y);
    return 0;
}

Output

Start:                   x = 10, y = 20
  inside swapByValue:     a = 20, b = 10
After swapByValue:       x = 10, y = 20   (NOT swapped)

  inside swapByReference: *a = 20, *b = 10
After swapByReference:   x = 20, y = 10   (swapped!)

๐Ÿ“ 2-mark questions and answers

What is a function?A self-contained block of code that does a specific task. It can take inputs (arguments) and return one value. Example: int add(int a, int b).
What is a function prototype? Why is it needed?A declaration giving the return type, name and parameter types, ending with a semicolon: int add(int, int);. It tells the compiler about the function before it is used, so calls can be checked.
Difference between actual and formal parameters?Actual parameters are the values passed in the call (add(3, 2)). Formal parameters are the variables in the definition that receive them (int a, int b).
What is recursion? Give an example.A function calling itself, with a base case to stop. Example: fact(n) = n * fact(n-1), with fact(1) = 1.
What is a base case?The condition where a recursive function stops calling itself and returns directly. Without it you get infinite recursion and a stack overflow.
What is a pointer?A variable that stores the memory address of another variable. int *p = &x;
What are the & and * operators?& is the address-of operator and gives a variable's address. * is the indirection (dereference) operator and gives the value stored at an address.
What is a NULL pointer?A pointer that points to nothing: int *p = NULL;. It's a safe starting value, so you can check if (p == NULL).
What is pointer arithmetic? If int *p = 1000, what is p+1?Adding/subtracting integers to pointers moves by whole elements. p+1 = 1000 + sizeof(int) = 1004.
Why is a[i] the same as *(a+i)?The array name is the address of the first element. Adding i moves i elements ahead, and * reads the value there.
Difference between call by value and call by reference?Call by value passes a copy, so the original is not changed. Call by reference passes the address, so the function can change the original.
Why must we use -lm for math.h?Math functions are in a separate library (libm). -lm tells the linker to include it.
What does strcmp return?0 if both strings are equal, a negative value if the first comes before the second (by ASCII), and a positive value otherwise.

๐Ÿ† Mini quiz

๐ŸŽฏ Practice homework

  1. Write a function int isEven(int n) that returns 1 if n is even, else 0. Call it for 1 to 10.
  2. Write a function float area(float r) that returns the area of a circle (3.14 ร— r ร— r).
  3. Write a recursive function to find the sum of digits of 4721 (answer 14).
  4. Write a recursive function power(base, exp). Test power(2, 10) = 1024.
  5. Use a pointer to find the biggest number in {4, 19, 7, 3, 12} without using a[i].
  6. Write void doubleIt(int *n) that doubles the original variable.
  7. Write your own myStrlen(char *s) using only pointer arithmetic.
  8. Sort the attendance list (array of pointers) alphabetically using strcmp and pointer swaps.