< B / >

Learning to C

Would you like to learn to C?

Let’s go.

§ -1. This is not your normal guide

This guide is weird.

It’s written to get a smart and persevering person started with the nitty-gritty side of programming that doesn’t get discussed as often.

Its goal is to supply a lot of information, tasks, and real-world projects while using real-world tools. For example, this means that this guide will introduce git when I think the time has come (which is when you would encounter it in real life). git, therefore, will probably be introduced while doing a project.

For this, this guide will alternate between:

  • Giving a high-level overview (where we’re coming from, where we’re going)
  • And then diving into guides/projects
  • Utilizing whatever good guide/task sheet/project description there already is.
  • And supplying the things I found missing when I first learned all of this.

When following this guide, things should become harder and harder, but you shouldn’t ever be clueless. If that happens, it’s not your fault, it’s mine. I just made a wild guess on how hard something would be which turned out to be wrong. If that happens to you, it probably will happen to other people reading this, too. When something irritates you, please tell me about it so that I can correct it. (Or, as soon as you’re that advanced: Just open a pull request on GitHub ^^)

Remember, this guide assumes you’re smart—behave like a smart person:

  • Whenever you encounter a problem and have thought about it for a few minutes, you google it. (Good sites include StackOverflow)
  • When you don’t know how something works in general, try to read its documentation (online or via man pages—we’ll get to that)
  • When you really get stuck, ask for help. Ask people you know that might know the answer, or just ask me directly. When asking questions there’s a few things to keep in mind: 1, 2, and every site’s got its own FAQ on asking (StackOverflow, for example)

Oh, and just to do some expectation management up front: Don’t be fooled by how short this guide appears to be. Expect that it will will be enough material for months.

§ 0. Setup

§ A. Unix

Get your hands on a Unix system.

This means either:

§ B. Terminal

Open up a Terminal Emulator.

It should show something like this:

Terminal window
username@system:~ $

Verify everything works by typing your first shell command:

Terminal window
username@system:~ $ ls

This should list the contents of your home directory—you should recognize some folder names.

And with that, we’re ready for Part 1: Navigating the command line

§ 1. Navigating the command line

Well, for navigating the command line, there’s a pretty good guide available.

The way this is going to work is the following:

The next guide has chapters, and after enough content has been handled, I’ll ask you spaced repetition questions to increase your learning.

  1. Work through 1. Introducing the Shell and 2. Navigating Files and Directories of The Unix Shell | Software Carpentry.

Done?

Time for a review:

That was Orbit, a tool for blending learning with text. This guide assumes that you know all of these questions from now on (and then for forever)—so, please, check your email regularly from now on :)

And with that, on to the next part: 3. Working With Files and Directories

Done?

Then it’s review time, again:

And on to 4. Pipes and Filters:

Continue with 5. Loops:

Continue with 6. Shell Scripts:

Continue with 7. Finding Things:

Aand: Done!

But now, to really test your knowledge:

Complete the Twelve days of shell, and, if you’re feeling adventurous, as much of the cmdchallenge as you can (or want to) do.

And then, on to other things.

§ Intermezzo: Installing stuff

So far, everything you needed to use came preinstalled with your Operating System.

This is about to change.

At the end of this intermezzo, we’d like to have the program gcc (one of the C compilers) “installed”. At least that’s the term you probably know from Graphical User Interface (GUI) programs.

For our intents and purposes here, installing a program means “making it available for execution”. So, installing a program yields this difference:

Uninstalled:

Terminal window
$ hello
Command not found: "hello"

And installed:

Terminal window
$ hello
Hello, World

The normal way to do this would be to install the package via your OS package manager (most probably apt), but we won’t do that here.
To exclude a whole class of problems from happening whatsoever, we will install things via Nix.

So, let’s get back to your terminal window, and install Nix:

Terminal window
curl -fsSL https://install.determinate.systems/nix | sh -s -- install

After completing the installation process, you should be able to run

Terminal window
nix --help

and see the help.

With that out of the way, back to our original problem: Installing gcc.

  • Create a new folder in ~: learning-to-c
  • Create a new file in that folder: flake.nix
  • Start editing that file (you now know how). It should end up with the following content:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs =
inputs@{
flake-parts,
self,
...
}:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
perSystem =
{
config,
self',
inputs',
pkgs,
system,
...
}:
{
_module.args.pkgs = import self.inputs.nixpkgs {
inherit system;
config.allowUnfree = true;
};
devShells.default = pkgs.mkShell {
packages = [
# interesting part is here.
];
};
};
};
}

Now, add the package hello to the package list:

packages = [
pkgs.hello
]

Test whether hello exists:

Terminal window
$ which hello
which: no hello in (...)

But it doesn’t.

For that to happen, we need to enter the so-called Nix Development Shell we just defined in our flake.nix:

Terminal window
nix develop

This puts you in a new shell that has hello available:

Terminal window
$ which hello
/nix/store/...-hello-2.12.2/bin/hello
# Verify that it works:
$ hello
Hello, World!

Okay, nice.
And now, let’s finally install gcc.

packages = [
pkgs.hello
pkgs.gcc15
]

Another nix develop and it should be there:

Terminal window
$ which gcc
/nix/store/1d75h3ii...-gcc-wrapper-15.1.0/bin/gcc

And we’ve installed gcc.

Review time :)

§ 2. C basics, Part I

§ Hello World

Okay, so now we’ve finally got a C compiler available. Let’s write our first C program, and compile it with it.

You know the drill by now:

  1. Open up an editor
  2. Write the program
  3. Save the file

So, staying in the directory with the flake.nix file, after you’ve executed nix develop:

Terminal window
nano main.c

Add the following content:

#include <stdio.h>
int main() {
printf("Hello, World!");
return (0);
}

Save the file, And then, compile it:

Terminal window
gcc main.c

This produces a new file a.out you can execute via:

Terminal window
./a.out
Hello, World!⏎

And with this, you’ve completed the first part of basically any programming language introduction: The Hello, World! program

Now, let’s take some time to practice reading unfamiliar code. When trying to read unfamiliar code, you should:

  1. Check what you don’t understand
  2. Find out what to google
  3. Google that
  4. Try to explain the piece of code to yourself.

1., you need to do yourself.
For 2., LLMs can be quite helpful.
3. just needs proper googling, and 4., you can only do yourself (again).

In our example, that might mean:

  1. Getting that you don’t understand what happening on the first line
  2. Asking claude what up with that, and it telling you that this tells the C preprocessor to include a header file.
  3. Googling something among the lines of “C include statement”, “C header file”, “C preprocessor”, “C preprocessor header file”, “C header file example”
  4. And with this newfound knowledge: Trying to explain it again :)

Now: Well, do that.

And when you’re finished: Review time! :)

Draft in Progress

Writing in here is haphazardous, disjointed and sketchy.

It's probably a good idea to come back later.

§ Intermezzo: Editor

Using nano surely has gotten a bit tedious by this point. Right?

Let’s fix that and use an editor people actually use as their daily driver: Visual Studio Code (NeoVim/Emacs will come later in this guide)

  • Install VSCode
  • depending on windows/unix, setup conection to WSL
  • install the C extension
  • open up the project

And, just so that you’ve seen it once:

VSCode is pretty configurable, actually: The editor and extensions itself use two files, keybindings.json and config.json to do most of the stuff.

And with that: Onto some more functions.

§ 3. C basics, Part II

  • syscalls
  • strcmp
  • putchar

§ Intermezzo: Version Control

So far, you’ve only worked locally on one state of your file system.

In the future, you might want to:

  • work on different PCs
  • collaborate on a codebase with different people
  • jump back in time
  • work on several avenue/features/chunks of code separately

For all of this, we’ll use something called Version Control. The main Version Control System in distribution is something called Git.

  • With Git, you have repositories in which your code lives (similar to folders in which you can branch and time travel)
  • in those repositories, you store a snapshot of the folder in time (that’s called a commit)
  • those commits form a directed acyclic graph
  • you can traverse and manipulate this graph
  • you can have these graphs on different machines by adding remotes

Git has a command line tool, git, with which all of this happens.

Let’s start and use it.

First, install it. (Pause here and think: How can you install things again?)

Got it to work? Nice!

Let’s use it while starting a new project, minitalk

§ 4. A first project: minitalk

Let’s create a new project folder minitalk under your top level flake (the one with gcc and git):

Terminal window
mkdir minitalk
cd minitalk

Let’s start the work on the project. First, let’s create the first file: main.c, and then, open the project in our editor.

Terminal window
touch main.c
code .

§ Intermezzo: Testing

§ Intermezzo: Shell aliases

§ Intermezzo: Fish shell (+ abbreviations)