</>CodexBash
C
CBeginner12 min

Functions and pointers to variables

Writing functions with parameters and return types, prototypes, why arguments are copies, and using pointers to let a function change a caller's variable.

How a function call returnsstep 1/6
main
in = main
The program starts in main. The stack holds one frame: main's own variables.

Writing a function#

#include <stdio.h>

int square(int x) {
    return x * x;
}

int main(void) {
    printf("%d\n", square(7));    // 49
    return 0;
}

A function has a return type (int), a name, parameters with their types, and a body. return hands back a value and ends the function. A function that returns nothing has the return type void:

void greet(const char *name) {
    printf("Hello, %s\n", name);
}

Order and prototypes#

C reads a file top to bottom, so a function must be declared before it is used. Either define it above main, or put a prototype, its first line followed by a semicolon, at the top:

int square(int x);        // prototype: "this exists, defined later"

int main(void) {
    printf("%d\n", square(7));
    return 0;
}

int square(int x) {
    return x * x;
}

Arguments are copies#

C passes every argument by value: the function gets its own copy. Changing it inside the function does not affect the caller:

void reset(int x) {
    x = 0;              // changes the copy only
}

int main(void) {
    int n = 5;
    reset(n);
    printf("%d\n", n);   // still 5
}

Pointers: letting a function change a variable#

A pointer holds the address of a variable. &n is "the address of n", and *p is "the variable p points at":

void reset(int *p) {
    *p = 0;             // change the variable at that address
}

int main(void) {
    int n = 5;
    reset(&n);          // pass n's address
    printf("%d\n", n);   // 0
}

This is exactly why scanf needs &: it has to write into your variable.

Pointers are also how a function returns more than one result:

void min_max(const int *nums, int n, int *lo, int *hi) {
    *lo = nums[0];
    *hi = nums[0];
    for (int i = 1; i < n; i++) {
        if (nums[i] < *lo) *lo = nums[i];
        if (nums[i] > *hi) *hi = nums[i];
    }
}

// called as: int lo, hi; min_max(nums, n, &lo, &hi);

const int *nums promises the function will not change the array, and the compiler holds it to that.

Scope#

A variable declared inside a function, or inside any { } block, exists only there. Two functions can each have a variable called i without interfering.

Recursion#

A function may call itself, as long as each call works on a smaller problem and there is a case small enough to answer directly:

long long factorial(int n) {
    if (n <= 1) {
        return 1;            // the base case
    }
    return n * factorial(n - 1);
}

Without the base case it calls itself until the program runs out of stack memory and crashes.

Key points#

  • A function declares its return type and parameter types; void returns nothing.
  • Define functions before use, or declare a prototype.
  • Arguments are copies; to change a caller's variable, pass its address and use *.
  • const on a pointer parameter promises not to change what it points at.
  • Recursion needs a base case.

Next: arrays, the last of the basics.

Practise it

The same idea, in the order it is worth meeting it.

  1. Not startedHow many digitsEasy
  2. Not startedIs it a palindromeEasy
Create a free account to track your progress