My Approach to Teaching C
C-Course.com is obviously not the only C course on the internet; you can find many manuals and courses online, and I don't even mention the classic K&R book that I highly recommend. I started writing my own course just because I had experience of teaching C to university freshmen, and I had some idea of a specific sequence of topics that worked for me back then.
Pointers appear before scanf
C is a difficult language to teach. Even the basic "Hello, world!" program uses some concepts that we cannot realistically explain to the beginner: the idea of header files and #include preprocessor directive, and what exactly strings in double quotes are. I actually seriously considered starting the course with using putchar() as the only way to print anything, but thought it might be too much: writing and debugging a function that prints a number digit by digit without using printf is probably too difficult for beginners, and also, #include <stdio.h> is still there.
So, as much as I don't like handwaving, I still need to go through the first few magic subjects: #include <stdio.h> and printf accepting a string in double quotes. On that note, mentioning that printf can accept any number of parameters is also magic at this point: we can only talk about stdarg.h much, much later.
But handwaving through scanf("%d", &x) is not an acceptable idea: saying “just do this to read an int” pretty much guarantees that there won't be any understanding of why & is needed here, and what it means at all. So, I don't introduce scanf until much later in the course. First, we figure out the basic syntax: functions, return, then if, for, while, etc.; we use printf().
Then I introduce getchar() and putchar(); at this point, getchar() is the only input mechanism the students know. With getchar(), we can implement a function read_int() to read an integer, digit by digit, with result = result * 10 + (c - '0'); and it helps us start writing programs that actually take input.
But then, once the students are comfortable with basic syntax, we must talk about pointers. And it's not scary at all! I introduce int * and show how to change the int value using the pointer. We discuss that all parameters in C are passed by value, and that it means that a function, when called as f(x), cannot change the value of x in the caller: C++ has references (int& x) but C has nothing like that. And then we can change our read_int() to accept a pointer to an int instead of returning the value. From here, introducing scanf is very logical and brings no magic, except for the variadic parameters, which is something we already had to handwave about earlier.
Algorithms and data structures help explain language features
I might be biased because I taught freshmen, but I believe you need to talk at least a little bit about very basic algorithms when you are teaching programming. While talking about arrays, it's very natural to implement linear search, but how can you avoid talking about binary search if you know your array is sorted? Some sorting algorithms, especially heapsort, probably deserve inclusion too (I haven't done that yet here), but I still need to be careful because this is a C course, not an algorithms course.
But then, hash tables. Hash tables are magical and very easy to implement; the linear probing in an array is my favorite method of implementing hash sets in Java on LeetCode. It's simple, easy to understand, shows a nice progression from the naive word counter to a faster version, and serves as a good example of putting several C language features together.
I also talk about linked lists; this topic does not bring anything new from the C language perspective, but it's the first real use of the struct keyword in the course, and also allows us to talk about trees and graphs later. Again: I'm not teaching algorithms, but you probably need to write depth-first search and breadth-first search at least once in each language you use get a better feel for the language.
Running code in the browser
This is the most questionable choice here, and I gave this a lot of thought. C is a very low-level language, it's probably closer to assembly language than to modern high-level languages such as Python or JavaScript. Won't I kill the whole idea if I let students run it in browser?
Probably. But the idea of being able to write your code right here, compile it, and run it, is something that people see on competitive programming websites all the time (including LeetCode, which I mentioned above). It does not really matter to the user whether the code runs on the backend server, or right in their browser; it feels the same, and you don't need a crash course in Bash and gcc or clang before you start writing code. Since I don't want to execute arbitrary code on my backend, I just compile the code to WASM and let the user's browser run it in a web worker.
The introduction of fopen is probably where we need to start talking about using a real compiler. I did implement some basic file support right here, though, so simple programs that read and write files can still be debugged in the browser.
It's a work in progress
This course is a work in progress, and even though I have already written enough material to take a student from zero to linked lists and hash tables, I still have many topics I haven't even touched. As long as I find an interesting challenge related to teaching C, I will update this post. For now, enjoy the course!