UP | HOME

Functions
COP-3223H

Table of Contents

What is a function in C?

  • Named computation

There are languages with anonymous functions as well.

Abstraction

Replace a chunk of code with a name.

Define it in terms of its input and output.

Allows us to think and reason about the whole computation a function does as one, instead of all of its individual computational steps.

Benefits

  • Reuse
  • Organization

Naming a chunk of code

We can name code, i.e., define a function by wrapping it in curly braces:

myfunction() {
  printf("hello, world!\n");
  printf("1 + 1 = %d\n", 1 + 1);
}

We can use, i.e., call a function that code by writing the name:

int main(int argc, char **argv) {
  myfunction();  // call the function
}

In C, parentheses are used for both the function definition and call.

They can optionally take arguments for the inputs (and outputs in C) of the code snippet.

Notice that main itself is also a function. It is called by the operating system when your program is run, which is why we put the code we'd like to run first there.

Reuse

Functions allow reuse without having to copy code

int main(int argc, char **argv) {
  myfunction();
  myfunction();
  myfunction();
}

Reuse with specialization

What if a snippet of code uses a variable?

myfactorial() {
  int product = 1;
  // n is defined somewhere else
  for (int i = 1; i <= n; i++) {
    product = product * i;
  }
  printf("product = %d\n", product);
}

We still want to be able to define a function without having to define all the variables is uses. This allows for the function to generalize beyond a single computation.

Functions can take inputs

We can declare special variables for the inputs

myfactorial(int n) {
  int product = 1;
  for (int i = 1; i <= n; i++) {
    product = product * i;
  }
  printf("product = %d\n", product);
}

Declare the special input variables in between the parentheses.

Give functions inputs when calling them

Giving the input is called passing arguments:

int main(int argc, char **argv) {
  myfactorial(3);
  myfactorial(5);
  myfactorial(2);
}

Functions can produce an output

// prefix name with the type of the return
int myfactorial(int n) {
  int product = 1;
  for (int i = 1; i <= n; i++) {
    product = product * i;
  }
  // use the return keyword to specify the output
  return product;
}

In C, the arguments can also be used to specify outputs by passing arguments by reference. This can be seen later when introducing pointers.

Using function output

The function can be used in expressions

int main(int argc, char **argv) {
  int result = myfactorial(5) + 10;

  printf("%d\n", result);
}

Think of the call like a variable, where it is replaced with it's value before the expression is computed.

Another example: exponent function

int exponent(int x, int e);

Multiple parameters delimited by comma

Using the abstraction

We can write code that uses a function before its implemented

int exponent(int x, int e);

int main(int argc, char **argv) {
  int a = 3;
  int b = 4;
  int csquared;

  csquared = exponent(a, 2) + exponent(b, 2);

  printf("csquared: %d\n", csquared);
}

The code won't run, but this shows the benefits of the function abstraction. We can ignore the low-level details of how exponent works, and focus instead on the code for the Pythagorean theorem.

Implementing exponent

int exponent(int x, int e) {
  int result = 1;
  for (int i = 0; i < e; i++) {
    result = result * x;
  }
  return result;
}

int main(int argc, char **argv) {
  int a = 3;
  int b = 4;
  int csquared;

  csquared = exponent(a, 2) + exponent(b, 2);

  printf("csquared: %d\n", csquared);
}

Delimit multiple parameters with a comma.

void functions

void is used when functions take either no input or output (or both)

void print_int(int x) {
  printf("%d\n", x);
}
int read_int(void) {
  int x;
  scanf("%d", &x);
  return x;
}

int is the default output type of functions

Combining abstractions: struct and functions

  • struct creates new data types
  • functions declare input and output types
struct point {
  int x;
  int y;
};

struct point shift_up(struct point p) {
  p.x = p.x + 1;
  return p;
}

chase with sprites

// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example

#include <stdio.h>
#include <stdbool.h>

#include "term.c"

struct character {
  int state;
  int x;
  int y;
  char description[128];
  char sprite[3];
};

void draw_character(struct character c) {
  printf("\033[%d;%dH", c.y, c.x);
  for (int i = 0; i < 3; i++) {
    putchar(c.sprite[i]);
  }
}

int main(int argc, char **argv) {
  set_input_mode ();
  printf("\033[?25l"); // hide cursor

  int width = 40;
  int height = 20;

  struct character player;
  struct character enemy;

  enum { vulnerable, invincible, dead };
  player.state = vulnerable;
  player.x = width / 2;
  player.y = height / 2;
  player.sprite[0] = ':';
  player.sprite[1] = '-';
  player.sprite[2] = ')';

  enum { chasing, running, defeated };
  enemy.state = chasing;
  enemy.x = 1;
  enemy.y = 1;
  enemy.sprite[0] = '>';
  enemy.sprite[1] = ':';
  enemy.sprite[2] = '(';

  struct {
    struct character character;
    int counter;
    int random;
    int str[10];
  } potion;

  enum { available, using, done };
  potion.character.state = available;
  potion.character.x = 29;
  potion.character.y = 11;
  potion.character.sprite[0] = '*';
  potion.character.sprite[1] = '-';
  potion.character.sprite[2] = '-';
  potion.counter = 0;
  potion.random = 1;

  while (1) {
    printf("\x1b[2J");
    printf("\x1b[H");

    draw_character(player);
    draw_character(enemy);
    switch (potion.character.state) {
    case available:
      draw_character(potion.character);
      break;
    }

    // debugging output
    printf("\033[%d;%dH", height + 1, 0);
    printf("player.y: %d\n", player.y);
    printf("player.x: %d\n", player.x);
    printf("enemy.y: %d\n", enemy.y);
    printf("enemy.x: %d\n", enemy.x);
    printf("potion.character.y: %d\n", potion.character.y);
    printf("potion.character.x: %d\n", potion.character.x);

    // read input
    int direction_x = 0;
    int direction_y = 0;
    bool valid = true;
    do {
      char c = '\0';
      scanf("%c", &c);
      switch (c) {
      case 'i':
        direction_y = -2;
        break;
      case 'k':
        direction_y = 2;
        break;
      case 'j':
        direction_x = -2;
        break;
      case 'l':
        direction_x = 2;
        break;
      default:
        valid = false;
        break;
      }
    } while (! valid);

    switch(player.state) {
    case vulnerable:   // case 0:
      player.x += direction_x;
      player.y += direction_y;
      break;
    case invincible:  // case 1:
      player.x += direction_x;
      player.y += direction_y;
      if (enemy.x - 1 <= player.x && player.x <= enemy.x + 1 &&
          enemy.y - 1 <= player.y && player.y <= enemy.y + 1) {
        player.state = vulnerable;
        enemy.state = defeated;
        potion.character.state = done;
      }
      break;
    }

    switch (enemy.state) {
    case chasing: // chasing
      if (player.x > enemy.x) {
        enemy.x += 1;
      }
      if (player.x < enemy.x) {
        enemy.x -= 1;
      }
      if (player.y > enemy.y) {
        enemy.y += 1;
      }
      if (player.y < enemy.y) {
        enemy.y -= 1;
      }

      if ((player.x == enemy.x || player.x == enemy.x + 1) &&
          (player.y == enemy.y || player.y == enemy.y + 1)) {
        player.state = dead;
      }
      break;
    case running:
      if (player.x > enemy.x) {
        enemy.x -= 1;
      }
      if (player.x < enemy.x) {
        enemy.x += 1;
      }
      if (player.y > enemy.y) {
        enemy.y -= 1;
      }
      if (player.y < enemy.y) {
        enemy.y += 1;
      }
      if (enemy.x < 0) {
        enemy.x = 0;
      }
      if (enemy.x > width) {
        enemy.x = width;
      }
      if (enemy.y < 0) {
        enemy.y = 0;
      }
      if (enemy.y > height) {
        enemy.y = height;
      }
      break;
    }

    switch (potion.character.state) {
    case available:
      if ((player.x == potion.character.x || player.x == potion.character.x + 1) &&
          (player.y == potion.character.y || player.y == potion.character.y + 1)) {
        player.state = invincible;
        enemy.state = running;
        potion.character.state = using;
        potion.counter = 10;
      }
      break;
    case using:
      potion.counter -= 1;
      if (potion.counter == 1) {
        player.state = vulnerable;
        enemy.state = chasing;
        potion.character.state = available;
        potion.random = (potion.random * 97) % 65533;
        potion.character.x = potion.random % width;
        potion.random = (potion.random * 97) % 65533;
        potion.character.y = potion.random % height;
      }
      break;
    }
  }
}

Author: Paul Gazzillo

Created: 2026-04-17 Fri 06:56

Validate