The C Programming Language book cover

The C Programming Language

Brian W. Kernighan and Dennis M. Ritchie

Second Edition, 1988 ISBN: 978-0-13-110362-7 272 pages
Overall Progress 10 of 10 chapters (100%)
Started: 02 Jan 2026
Completed: 04 Jan 2026
Last updated: January 2, 2026

This is the C book. The holy grail.

This is the book that I have gotten the most recommendations about. While it may be somewhat dated (i mean duh, it’s from 88), it is incredibly relevant and the exercises in the book, should be excellent.

It has been on my to-read list for ages and it is better than never to get started now.

The book opens with being very explicit about its age, but also being very blunt about the fact that C is a small language, and as thus shouldn’t have a very thick book. I have also planned to read the SICP book, which is similarly old.

The preface and the introduction is also rather short and straight to the point, but really emphasizes how simple C is intended to be - and how quirky some of the things can be. But also how charming that can be. It is simple to learn and incredibly difficult to master.

Notes after having worked through the book

The later chapters reads very much like a reference manual and feels very very dated, so I decided to glance over the later chapters, so I can move on to working through another book I have on my shelf, which is more recent.

Difficulty
★★☆☆☆ Easy

Chapter 1: A Tutorial Introduction

Completed: 2025-01-03 Exercises: 14/24 3,5 hours

So this chapter intends to set us up for success, by giving a breezy introduction on how to get running and learning most of the language.

Compiler

It starts by assuming we have access to cc. It seems that the C Compiler used at the time, predates GCC, but now essentially uses that. We can verify this as such:

$ cc --version
cc (GCC) 15.2.1 20251112

To make it easy for myself, I have created a new repo for the exercises and will be making a folder for each exercise and add a main.c file in there. That way, it will be trivially simple to compile and execute the program in a one-liner:

$ gcc main.c -ansi -pedantic -o main.out && ./main.out

Having -pedantic enforces standards compliance and -ansi forces the specific version that is used in the book (which is equivalent to -std=c90 - which we know as C89).

Exercises

Exercise 1-2 writes about escape sequences and there’s definitely some of them I didn’t know about. There’s also a pretty interesting historic article about printf on wikipedia. The name apparantly comes from 1956 and Fortran and is literally asking to print on the attached printer.

So there’s definitely a lot of convenience added in later versions of C. Exercise 1-5 highlights that inline declarations for loops aren’t allowed:

error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode

In Chapter 1.5.2, the book talks about using double for counters, due to int being 16 bit at the time, being limited to 32768 and thus subject to overflows rather quickly. This isn’t the case these days with 32 bit being the default in most systems. This is why some C programs these days start out with defines, that has asserts to check the size of basic types.

Exercises 1-8, 1-9 and 1-10 feels like the brain teaser that some of the easier leetcode questions might be. Those were fun!

Exercise 1-13 and 1-14 utilizes arrays, and here I got to look at zero initialization of arrays, as a thing. The book example declares it, and then loops over the array and sets the values to 0. I think I’d just do like this:

int foo[256] = {0};

The rest of the chapter introduces functions, arguments, char[], scopes/extern and declares that this is the “core” of C, allowing to built most things from this simple core set. That’s a bold claim. Let’s see where this takes me in the next chapters.

As I have some experience in programming, I will be skipping the rest of the introduction exercises for chapter 1 to move on.

Chapter 2: Types, Operators, and Expressions

Completed: 2025-01-03 Exercises: 1/10 45 minutes

This chapter starts with declaring that constants are all UPPERCASE and variables are all lowercase, using underscores for seperating. This is where we get into religion for how code is formatted. Personally, I don’t care - as long as there is a linter/formatter to make sure it is consistent across the entire codebase.

This is also where the book shows its age. With 64bit being the standard, we are very much used to ints being 32 bits and the availability of 64 bit ints and in a few languages (like Odin) even 128 bit integers.

Exercise 2-1 asks me to look at the size of short, int and long. In the time of the book, a short would be 16 bits, a long 32 bits and an int 16 or 32. The wording is quite special, as it states it would be “at least” said bits. This really explains why I see so many codebases having asserts on the size of the data types. The page in integer types writes about how it was standardized in C99 with fixed width, like int16_t and uint64_t.

The documentation page above also talks about “fast” and “least” versions of those types. I’m not going to dive into that now. I’ll probably read about that in another book.

The first “new” thing for me, is if I declare an argument in a function const, I send a signal to the programmer (myself), that no change to the argument can happen. What happens when one does perform a change, is compiler specific and not described in the book.

This doesn’t make any difference for when passing in primitives, but as soon as I start sending pointers and arrays in there, I think it will make a big difference in my way to think about it.

The things from <ctype.h> I used in 1-14 is now described in chapter 2.7 - I went a bit too quickly with that one, oops.

I did the first exercise to look at the size of types, but won’t be doing the rest of the exercises.

Chapter 3: Control Flow

Completed: 2025-01-04 45 minutes

This chapter opens with coming with a lot of examples of why leaving out braces is horrible, when having multiple nested ifs. With experience, this becomes pretty obvious. I think we’ve all made these mistakes before.

And after talking about if and else, it slaps binary search in there. Bam! A complete beginner would be absolutely lost right here. This is why I didn’t give it a 1 star in difficulty, but a 2.

Chapter 3.4 introduces the switch statement. Having watched Handmade Hero, he talks about a habit that will save your butt many times in the future for switch statements. Below is an example in the style (which I have adopted byself):

switch(c){
    case 'a': {
        /* do something */
    } break;
    case 'b': {

    } break;
    case ' ':
    case '\t':
    case '\n': {  /* intentional fallthrough */
        handle_whitespace();
    } break;

    default: {
        /* handle default case */
    } break;
}

By using brackets, it also adds in scoping as another protection against unintentional code.

In chapter 3.5, the book introduces a sorting algorithm. And it chose shellsort as its first one to introduce. Reading about it, it seems to have very little use today. But due to the fact that it doesn’t use the call stack and can be implemented using very little code, it is a good fit for embedded systems.

In general, a short chapter that has a few challenges for it’s exercises. Some that I most likely will be implementing at a later point anyway.

Chapter 4: Functions and Program Structure

Completed: 2025-01-04 1,5 hours

One of the first things that is really annoying with C (and in extension also C++) is the fact that you can’t use a function/method if it isn’t declared above where you want to use it. Either included or forward declared.

Many other languages does some black magic compiler shenanigans, so we don’t have to forward declare anything.

Moving forward in the chapter, the book really shows its age, with concepts like the register, which was intended to tell the compiler that a variable was used a lot. Back then, compilers weren’t as smart as they are today.

This is also where recursion is introduced, but due to many years of Advent of Code, I’ve become very comfortable with recursion. It does take quite a while for this to click for beginners.

There is a very little section on macros. This is definitely something I need to practice at some point, as this is tricky to get right.

The chapter ends with explaining include guards, which is way to avoid double includes. These days, #pragma once is preferred (altho not a direct part of the standard, it is implemented by most compilers).

Chapter 5: Pointers and Arrays

Completed: 2025-01-04 1,5 hours

Roughly four months ago, I started doing ThePrimeagen’s course on Data Structures, and forced myself to implement everything in C. This forced me down the rabbit hole of understanding pointers. And something just clicked, when I worked with it.

It was a great ride and I highly recommend it.

I don’t know why the biggest mental blocker for me, was understanding void* - which in its simplicity, just is a pointer to anything.

Halfway through the chapter, the complexity takes a large step up. The concepts get quite abstract, but since I’ve played with pointers before - I can still follow along.

Reading more, it is just a lot of code samples recreating standard library things, like strlen, strcopy and more. If unsure about pointers, the exercises seem like a great choice. But I’m currently focusing on getting through the book, so I can move on and get a lot of code written using things learnt.

Lastly, the chapter moves into really complex stuffand literally calls it “complicated declarations”, and it’s stuff like char (*(*x[3])())[5]. Gotta focus when trying to grasp what on earth it means. But it’s all about breaking it down slowly.

Chapter 6: Structures

Completed: 2025-01-04 1 hour

This chapter introduces structs. I’ve used them quite a bit already, so I don’t expect much new to be introduced.

It does almost straight jump to introducing a tree and a hashtable. Essentially linked lists, which I covered during primeagens algorithms course, so I’m good with that for now.

Then it jumps to typedefs, unions and bitfields. There might be some usage for unions in the things I want to build and most certainly for bitfields. As far as I know, graphics APIs and OS APIs have these all over the place, so it’s in heavy use.

While the book advocates for using bitfields for saving storage space, I’d say that they also are great due to their raw performance.

Chapter 7: Input and Output

Completed: 2025-01-04

This chapter reads more like a reference manual, to be perfectly honest. While it does introduce it once, I am fairly certain that the topics in here are the ones that you’ll implement once and not do again for months, only to look up documentation on how it works later.

Chapter 8: The UNIX System Interface

Completed: 2025-01-04

Chapter 9: Appendix A - Reference Manual

Completed: 2025-01-04

Chapter 10: Appendix B - Standard Library

Completed: 2025-01-04
Updates & Revision History
  • 04 Jan 2026: Worked through the rest of the book
  • 04 Jan 2026: Completed Chapter 1, 2 and 3
  • 02 Jan 2026: Started the book