Brainlag
  1. C
  2. Foundations
05/38 lessons

Writing your own functions

25 min4 exercises

Learn

Anatomy of a function

You have called functions you did not write, like printf, and read a few small ones of your own. Now write them on purpose. A function is a named piece of work: values go in, the work happens, one value comes back.

C
int xp_needed(int level) {
    return level * level * 50;
}
  • int in front of the name is the return type, the type of the value handed back.
  • (int level) is the parameter list. Every parameter has its own type, separated by commas.
  • return hands the value back and ends the call on the spot. Nothing after it on that path runs.

Calling xp_needed(4) passes the argument 4. Inside, level is a fresh variable holding a copy of it, and it only exists while the call is running.

Learn

Types in, types out

Any type can go in and come out:

C
double hp_percent(int hp, int max_hp);   // two ints in, a double out
char grade(int score);                   // an int in, one char out
void print_bar(int filled, int width);   // nothing comes back

The return type converts the value: return 7.9; in an int function hands back 7, chopped like a cast. void means no value at all. The function does its job, usually printing, and the call is a statement on its own: print_bar(6, 10);. A bare return; leaves a void function early.

If a function says int and a path reaches the closing brace without a return, C does not stop you. The caller gets junk. Return on every path.

Learn

Declare before use

The compiler reads top to bottom once, so it must know a function before the first call. Either write the whole function above main, or put its prototype, the first line plus a semicolon, near the top and the body anywhere below. Many programs list every prototype first, so the file opens like a table of contents.

A function cannot see main's variables. Everything it needs comes in through parameters, and the answer goes back through return. If the caller ignores the returned value, it is simply gone.

Try

Run it: a character sheet

The example is already in the editor. Run it, change one thing, and run it again.

example.c to run
#include <stdio.h>

int xp_needed(int level);
double hp_percent(int hp, int max_hp);
void print_bar(int filled, int width);

int main(void) {
    int level = 4;
    int hp = 27;
    int max_hp = 40;
    printf("Level %d, level %d at %d XP\n", level, level + 1, xp_needed(level + 1));
    printf("HP %d of %d is %.1f percent\n", hp, max_hp, hp_percent(hp, max_hp));
    print_bar(hp * 10 / max_hp, 10);
    return 0;
}

int xp_needed(int level) {
    return level * level * 50;
}

double hp_percent(int hp, int max_hp) {
    return 100.0 * hp / max_hp;
}

void print_bar(int filled, int width) {
    printf("[");
    for (int i = 0; i < width; i++) {
        if (i < filled) {
            printf("#");
        } else {
            printf("-");
        }
    }
    printf("]\n");
}

Try

Damage after armour

In a dungeon game, armour soaks up that many points of every attack, but a hit always does at least 1 damage. Finish int damage(int attack, int armour) so it returns the damage that gets through. main is done. Expected:

  • goblin hits the knight for 3
  • dragon hits the knight for 31
  • bat hits the knight for 1
exercise_1.c to run
#include <stdio.h>

int damage(int attack, int armour) {
    return 0;
}

int main(void) {
    printf("goblin hits the knight for %d\n", damage(12, 9));
    printf("dragon hits the knight for %d\n", damage(40, 9));
    printf("bat hits the knight for %d\n", damage(3, 9));
    return 0;
}

PracticePredict the output

What comes back

Three functions, three rules from the lesson: a parameter is a copy, the return type converts, and return ends the call. Type exactly what this prints.

Read the program, type exactly what it prints, then lock it in. It runs after that.

exercise_2.c
#include <stdio.h>

int level_up(int level) {
    level = level + 1;
    return level * 10;
}

int split_loot(double gold) {
    return gold / 2;
}

void join_party(int size) {
    if (size >= 4) {
        printf("party full\n");
        return;
    }
    printf("joined, party of %d\n", size + 1);
}

int main(void) {
    int level = 3;
    int reward = level_up(level);
    printf("%d %d\n", level, reward);
    printf("%d\n", split_loot(7.0));
    join_party(2);
    join_party(4);
    return 0;
}

Break/fixFix the bug

A bonus that never lands

A combo of 10 hits or more earns 5 points per hit. The function is right, but the score never changes. Fix main so it prints:

  • after a 12-hit combo: 260
  • after a 3-hit combo: 260
exercise_3.c to run
#include <stdio.h>

int add_combo_bonus(int score, int combo) {
    if (combo >= 10) {
        score = score + combo * 5;
    }
    return score;
}

int main(void) {
    int score = 200;
    add_combo_bonus(score, 12);
    printf("after a 12-hit combo: %d\n", score);
    add_combo_bonus(score, 3);
    printf("after a 3-hit combo: %d\n", score);
    return 0;
}

Your turn

Judge a rhythm game

A rhythm game judges every note by how far off the beat you hit it, in milliseconds: negative is early, positive is late. Write three functions:

  • char judge(int ms_off) returns 'P' (Perfect) within 30 ms either side, 'G' (Good) within 80 ms, otherwise 'M' (Miss).
  • int combo_after(int combo, char result) returns the combo after a note: one more after a Perfect or a Good, back to 0 after a Miss.
  • double accuracy(int perfect, int good, int notes) returns a percentage of notes where a Perfect counts as a whole note and a Good as half a note, fraction kept. With 0 notes it returns 0.

On your own. Hints open after your first check.

exercise_4.c to run
Step 1 of 8
C, 32-bit: int and pointers are 4 bytes

Code editor. Press Control or Command plus Enter to run the code. Tab indents; to move focus out of the editor, press Escape and then Tab.