4 min read

Bare Metal Foundations 01 - Anatomy of a Program

A pixel art image of a programmer looking at some code on a screen.
🔗
This post assumes that you've completed Bare Metal Foundations 00 - Primer & Setup.

If you haven't, stop reading, do the stuff in that post (takes about 10 minutes), then come back here.

Before we start writing any code, let's quickly get back to our workspace.

Open your Terminal (Start > MSYS UCRT64 for Windows, Cmd-Space Terminal for Mac, however you normally do it for Linux) and run:

cd ~/code/bare-metal/foundations

# Now we will make a new directory & move into it
mkdir 01-anatomy-of-a-program
cd 01-anatomy-of-a-program

# And launch VS Code, pointing at a new file called return.c
code return.c

The lines starting with # are 'comments', if you copy & paste them into your terminal, they won't do anything. They're there for you, the reader.

A Simple C Program

🥷
It may feel tempting to copy & paste the examples below, but... avoid the temptation & type them out manually instead.

Studies show that copying information by hand helps us to retain the information longer-term. It will also help you to build up muscle memory, and if you write any typos, you'll get practice debugging them in simple programs instead of complex ones.

If everything went well, you'll be staring at an editor with an open file. We're going to write the simplest C program possible:

// return.c

/*
 * This is a function declaration, here's how it works:
 * - int is the return type: when we run this function,
 *   we return an integer (a whole number).
 * - main is the name of the function, all executables
 *   in C have a main function, which is where running
 *   starts.
 * - () is the parameter list. In this case we have no
 *   parameters, so it's empty.
 * - { is the opening brace of the function. The
 *   function code always goes between an opening brace
 *   and a closing brace to tell the compiler where it
 *   starts and where it ends.
 */
int main()
{
  /*
   * We are just going to return the value 0 from our
   * function. By convention, a main function that
   * returns 0 indicates a success.
   * 
   * The semi-colon (;) at the end of the line tells
   * the compiler that the statement is complete. This
   * means that we can write function calls &
   * statements over multiple lines.
   */
  return 0;
  /*
   * This is the closing brace, it tells the compiler
   * where our function ends.
   */
}

In C, there are different ways to write comments (notes for yourself or other programmers reading your code):

  • /* Comment here */ - this is a multi-line comment (although, confusingly, it can be a single line). The comment starts at /* and doesn't end until */.
  • // Comment here - this is a single-line comment. It reaches from the // until the end of the current line.

Now, save the file (Ctrl-S on Linux/Windows, Cmd-S on Mac), then switch back to your terminal, and run the following:

# Compile return.c and save the compiled program as return
clang return.c -o return

# Run return
./return

And you'll see... absolutely nothing. Running echo $? (this displays the return code of the last program in the terminal) will show 0.

Try changing return 0; in the program to return 69; or return 420; then compile & run again. You'll see echo $? update to show the return code that you specified.

🚨
Return codes are used to specify whether anything went wrong, and what went wrong.

By convention, 0 means that the program ran successfully.

You can return a different integer depending on what went wrong, for example 1 could mean invalid input, 2 could mean we ran out of memory, etc.

The Venerable "Hello, World!"

This is the first program that most people learn to write in any given programming language, so let's switch back to the terminal and run:

code helloworld.c

You should see a new tab in your VS Code window called helloworld.c, type the following program into the window:

// helloworld.c

/*
 * We need to include stdio.h in order to use the
 * printf function.
 *
 * All that #include does is take all of the code
 * in the specified file and insert it into this
 * file.
 *
 * stdio.h is a header file (we'll talk about that
 * later) with a lot of functions, but today we're
 * just using printf.
 */
#include <stdio.h>

int main()
{
  /*
   * printf is the standard way to write output to
   * the terminal from a C program.
   *
   * It can be called with many parameters, but here
   * you'll see that we are just calling it with one:
   *
   * "Hello, World!\n"
   *
   * This is called a string (we'll talk about it
   * later, but for now you can think of it as text).
   *
   * The \n is called an escape character, again we
   * will talk about those later, but this one means
   * 'new line'.
   */
  printf("Hello, World!\n");

  return 0;
}

Save the file (Ctrl-S/Cmd-S), compile it and run it in your terminal:

clang helloworld.c -o helloworld
./helloworld

You should see "Hello, World!" printed in your terminal.

Quick Recap

We've only written two simple programs, but we've learned some important fundamentals of writing computer programs:

  • We've learned how a function is declared in C:
    • return type (in our case int)
    • followed by name (in our case main)
    • followed by arguments (in our case none, or ())
    • followed by an opening brace ({)
    • int main () {
  • We've learned how to return a value from a function: return 0;
  • We've learned how to print text to the screen: printf("Hello, World!\n");
  • We've learned that main is where a program starts, and when it returns 0, that means it was successful.
  • We've learned how to compile our programs with clang, and how to run them.

We briefly touched on some other concepts which we'll talk in depth about later:

  • How #include works, and how to find what you're looking for.
  • How functions work, and how to work out how to call them.
    • printf is a particularly meaty one, don't let it fool you.

Next, we'll learn about different types of numbers & variables in C.