Categories
Programming

C Tutorial

Welcome to my introductory C programming tutorial.

This tutorial is intended to be a quick and dirty introduction so we can get into building cool stuff.

C was one of the first programming languages I ever tried to learn, and while it’s not as modern and easy to use as more recent programming languages, I still enjoy using it. It’s simple, fast and super powerful, letting you build everything from complex games and operating systems, to low level code that runs on embedded devices and rockets.

So let’s build something.

What is C?

Humans use spoken and written languages to communicate with each other, whether that’s professing our love for another person or following instructions to bake a cake, language is generally how we give and receive information.

While I personally speak English and can understand some very rudimentary Japanese thanks to years of Martial Arts training and occasionally paying attention in High School, computers can only understand machine code, which is a series of 1s and 0s. The sequences of 1s and 0s, or binary code, form instructions and contain data that the computers CPU can understand and execute. The CPU will fetch the machine code from memory and then execute each instruction one at a time (but really really fast), and these instructions make up the computer programs that we can use.

Machine code is the only language computers understand, but binary is super tedious and annoying, so humans invented programming languages that are part way between what we understand (words and whatnot) and what machines understand (1s and 0s). C is one such language.

C was invented in the 1970s by some really clever people over at AT&T Bell Labs for the creation of the Unix operating system. Unix was originally written in Assembly language, which is close to machine code, but writing complex programs in Assembly isn’t all that easy.

C was created specifically to write Unix, and because of that most of the modern technology we rely on, like the Internet, iPhones, Microsoft Windows, all that, is built on top of the foundation that C provides. Safe to say, without C our modern digital world wouldn’t be what it is today.

Prerequisites

Programming is a complex topic and C is not the easiest of ways to get started. There’s plenty of arguments for and against learning C as a first language, none of which I’m going to discuss.

If you want to learn C and don’t know another language, that’s great. C was my first programming language. Be aware though, that you might need to stretch your brain muscles a little more. C doesn’t hold your hand.

If you’re looking for a first language before C, I’d suggest looking at Python. Otherwise, let’s continue.

I assume you can use a computer at least somewhat competently. You should probably also be comfortable with Linux. Most of the C code in this series will be geared towards Linux. If you know what the terminal is and can navigate around using basic Unix commands you should be fine.

I also expect you can use the Internet.

Up and Running

To write C code, or any programming language, you need certain other tools available. Code that we (humans) write is basically just plain text and can be written much like you write an email or a Word document (just please don’t use Word to write C code).

For C programming you need a text editor such as notepad.exe or vim. You can use sophisticated tools called IDEs like Visual Studio or Eclipse for programming work, and maybe one day you will, but for now, a text editor is all you need.

Writing C code into a text file doesn’t really make a computer program though. Once you’ve written your C, you need to convert it into the machine code that computers understand. This step of converting C code to machine code is called compiling and for that we need a compiler.

A compiler is basically just another computer program that can take our C code as input and then provided we wrote proper C code, it spits out an actual program that we can run. There’s lots of different compilers that we can use, but for this tutorial I’m going to use the GCC compiler that can be installed on Linux.

You can do C programming on Microsoft Windows and Mac OS X, or pretty much any Operating System just in case you have a spare mainframe with z/OS lying around. I don’t really want to write instructions for every Operating System, so we’ll just use Linux. I’m going to use Fedora Linux, but you can use whatever Linux you want. The installation instructions may vary but the compilation steps should be mostly the same.

On Linux you can install GCC from your distributions package repositories, for example:

$ sudo yum install gcc

As you can see above, I first checked if GCC was installed (I knew it was because I’ve used it before) with the which command.

$ which gcc 

And then proceeded to install gcc anyway with the yum command. You can see near the bottom of the output saying

Package gcc-11.2.1-1.fc34.x86_64 is already installed.

Which says on my Fedora 34 system, gcc version 11.2.1-1 is already installed. If you’ve done that or something similar for your Linux distribution, ie with apt-get if you’re using Debian or Ubuntu, then you should be good to go. However if you type which gcc and don’t get something like /usr/bin/gcc as the response then you might need to consult the instructions for your particular installation.

Once you’ve got your compiler installed, it’s time to write some C.

First C Program

Recall above I mentioned you needed a compiler and a text editor. The text editor is where you write your C code and the compiler takes your C code and turns it into a real computer program.

So lets fire up your text editor and write some code!

As I’m using Linux (and I hope you are too), and I’m a bit masochistic, I’ll be writing my code into the vim text editor. You can use something else, like gedit or nano if you like, just please don’t use a Word Processor (no Libre Office).

From the terminal, open a text file called hello.c:

$ vim hello.c

And write the following super simple program:

#include <stdio.h>

int main()
{
    printf("Hello, C Programmer.\n");
    return 0;
}

Save the file and exit the text editor (if you’re using vim press esc and then :wq).

So that’s a simple C program. The program is intended to print the message “Hello, C Programmer” to the terminal, but there’s a lot of other rubbish that we need to explain first.

Before we do that though, let’s compile the program. Run the following command from the terminal after you’ve saved and exited your text editor:

$ gcc hello.c -o hello

This command calls gcc and tells it to compile the hello.c file. The -o hello bit that the end informs gcc that the output file should be called hello. If you don’t do that -o bit you’ll get a weird program called a.out, which will still work, just doesn’t look as good.

If everything went according to plan, you should have seen no output and you’ll have your command prompt back. Type the Linux command ls to check:

You can see hello.c which is the code file we wrote, and the green hello is the executable file that gcc created for us. Let’s run it.

$ ./hello

The ./ tells Linux (well, it tells the Bash shell), to execute the hello program which is in the current directory. If you don’t give it the ./ your computer will run around trying to find it and then give you an error. You should see this:

If you can see “Hello, C Programmer” then congratulations, you’ve just written and compiled a C program.

So what just happened? Let me try and explain what that C program meant. Recall our code:

#include <stdio.h>

int main()
{
    printf("Hello, C Programmer.\n");
    return 0;
}

An astute reader will notice the line starting with printf. This line is the message that we want our user to see when they run the program. printf is a “function”, or a command, built into the C standard library that lets us print to the screen. Well there’s a little more to it than that, but for now, it prints to the screen.

But what’s the rest of the stuff we wrote?

C needs a bit of extra help setting things up before it can print your message or run whatever your program is. This isn’t necessarily a bad thing and there is a reason for it all, but at first it can make C a little more confusing than other languages such as Python (which doesn’t need most of the other stuff).

Let’s start at the top.

The first line of the program says #include <stdio.h>.

I mentioned a couple of paragraphs ago that printf was built into the C standard library. By itself, C is pretty simple and can’t even print stuff to the screen. C is meant to be a systems programming language, and when you’re writing something like a low level device driver for a piece of hardware you generally don’t need the same kind of functionality that, say a game would need. So for efficiency, C strips out pretty much everything and on its own doesn’t give you much. However, along with C comes a library of extra stuff to help you write your programs, things like pre-existing code that helps you print stuff, or do maths calculations, or work with strings of text.

The catch is, all that extra stuff needs to be “included” in your code if you want to use it. For our purposes, we want to write a line of text to the screen rather than perform complex 3D math, so we need the standard input output library, or stdio. The #include tells C to “include” the stdio.h header file into our code. Header files are basically definitions of other code, which is a story for another day, for now though, we need to include the header file, stdio.h, into our code so we can get access to the functionality that handles input and output. Make sense?

The next part of our program, int main() is the actual start of our code. There’s whole lot of explanation that needs to go along with this line in order to properly understand what it is and why, but the simple explanation is, C starts running your code from here, everything inside the { and } is where you put your code and when the computer runs your program, it jumps here first. This is called the entry point to your program. When we get into functions later I’ll revisit this explanation, but for now, int main() means “start here”.

Inside the main block in between the curly braces is our actual program. Remember how we told C we want to access the standard input output library, well here it is, live and in action. printf is a command to C to output whatever is in between the parenthesis to the screen. We give it our message, and C prints it.

The last line, return 0 is another one of those things I’ll need to save for later to explain, but for now, the main() part of the program requires this line so that the operating system (Linux) knows that the program finished properly. Basically return just tells Linux that our program is finished and it can move on to something else.

While fairly simple, this is a complete C program and contains what pretty much every C program you’ll ever write has.

There’s include statements to bring in additional code, such as built in libraries or other code files you or someone else wrote.

There’s a main() function, which contains other statements and functions that perform the actual work of your program.

At the very end there’s a return statement to finish up.

A Simple App

In the last section I explained a bit about what C is and why it exists, we also wrote a really trivial and basic first application that you’ll no doubt see in any C tutorial ever.

Most books I’ve read on C and programming in general tend to start off really quite boring, requiring you to read dozens of pages of explanations about data types, statements and expressions before giving you even the most simple of programs.

Personally, I learn better by actually writing code. That’s how I started back when I was trying to learn game programming as a kid. I didn’t care what an integer was, I just wanted to see something move. Those other details are important, but we can fill them in as we go.

In this section I wanted to step it up a bit. We’re going to build a real C program that actually does something semi useful.

The program we’re going to build is a simple note taking app. Sure, I know what you’re going to say…

“A note taking app… WTF!!”

Yes, a note taking app. I realise this doesn’t sound fascinating but for a first real C program it’s simple enough that we can get up to speed and build the thing in a reasonable amount time without being an expert in C, but it’s difficult enough to be challenge and will teach us the fundamentals of how a real C program works.

This note taking app will have many of the characteristics of a typical Unix program. It will accept input from the command line, store data for the user in a simple text file, and output the data when the user requests it. However, as this is intended to be a simple application for learning purposes it won’t be as robust or as correct as it could be. This is on purpose, we aren’t building production code just yet, we just want to get our prototype working.

Before we write any code though we need to design what our program is going to do. Almost all computer programs follow a similar set of steps, which we can use to map out how we want our program to work.

Initialization

The initialization step of a program is when a program sets itself up ready for action. It will initialize any state, or data, that it requires or sets up any external network connections.

In the case of our note taking app, we don’t really have any initialization just yet but we will later.

Input

Programs work with data, and that data needs to come from somewhere. Whether that’s input from a user, read from a file, or accessed over a network.

Our note taking app will accept user input from the command line using the Unix standard input. Without going into too much detail about how Unix works, the standard input is just the source of input from the command line, typically from the keyboard as the user types but can also be sent from a file or other command using pipes and redirection. When the user runs our program they will be expected to enter a command, and in some cases they will need to enter additional data. For example:

$ ./note read

The read command will be built into the note taking app and doesn’t require any other arguments. When the application is told to read it will open up the users data file and read the list of notes. For now to keep things simple it will just read every line one at a time until the end of the file. Later on we can make it a bit more sophisticated.

$ ./note create "This is a note"

When the user types create they application expects extra data to come next. This is the note that the user is making. The note is just a string of text that will be saved to the text file.

Processing

Once the program has data to work with, it can set about performing any processing actions on the data. Programs are normally created to achieve a specific purpose and the processing is generally where that purpose takes place. These are often algorithms that transform or manipulate the data in some way.

Our note taking app wont be doing much processing just yet, for now it’s Ok to assume that something will happen, but we are only saving and reading data from the file.

Output

After the processing is complete, the program will send its results back to the user, or save them in a text file or database.

This can be thought of as the note taking app saving notes to the text file, and also reading the text file and displaying the contents to the screen. For now we don’t give the user the option of creating multiple text files or even changing the name of the text file, but we will later on.

Termination

This is where the program cleans up after itself, closes any open files or connections and finishes its execution, handing control back to the Operating System.

Our note taking app will need to close the open text file and finish executing.

The Program

Open a file called notes.c with your text editor and write the following program:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void usage();
void create_note(char *note);
void save_note(char *note);
void read_note();

/**
 * Main program entry point
 */
int main(int argc, char *argv[])
{
    // Check that the user has entered a command
    if (argc < 2) {
        usage();
        return EXIT_FAILURE;
    }

    // if the user enters "help" display usage information
    if (strcmp(argv[1], "help") == 0) {
        usage();
    }
    // If the user enters "create", create the note
    else if (strcmp(argv[1], "create") == 0) {
        if (argc != 3) {
            fprintf(stderr, "Please enter something.\n");
            return EXIT_FAILURE;
        }
        else {
            create_note(argv[2]);
        }
    }
    // If the user enters "read" open the note and read the data
    else if (strcmp(argv[1], "read") == 0) {
        read_note();
    }
    else {
        usage();
    }

    return 0;
}

/**
 * Display program usage information
 */
void usage()
{
    printf("notes [option] <argument>\n");
    printf("example: notes create \"This is a note\"\n");
}

/**
 * Create the note.
 * For now we're simply taking the input and passing it to the save
 * function.
 */
void create_note(char *note)
{
    save_note(note);
}

/**
 * Open the text file and save the note
 * Notes are appended to the end of the file to preserve existing notes
 */
void save_note(char *note)
{
    FILE *fp;
    fp = fopen("notebook.txt", "a");
    fprintf(fp, note);
    fprintf(fp, "\n");
    fclose(fp);
}

/**
 * Open the text file and read the notes to stdout
 */
void read_note()
{
    FILE *fp;
    char buff[255];
    fp = fopen("notebook.txt", "r");
    while(fgets(buff, 255, (FILE*)fp)) {
        fputs(buff, stdout);
    }
    fclose(fp);
}

Once you’ve written the code, you can save and exit the file and then compile the code similar to how we did with the first program we wrote.

$ gcc notes.c -o notes

Provided you haven’t missed anything or make an error, everything should compile correctly and give you a program to run called notes. You can try running it as mentioned above in the program design steps and see how it works.

So what did we just write?

Program Explanation

There’s a lot more going on in this program than our previous one, and now I’ll be forced to explain the pieces in more detail, this may take some time so bare with me.

At the top, just like before, we’re including additional libraries into our code. You can see the stdio file which gives us things like printf, but there’s also string.h and stdlib.h. string.h is the C standard string library, strings are just text characters like “this is a string”, and the string.h library gives us functions to work with strings, in this case we’re using the function strcmp which means “string compare” and allows us to compare two strings which is used further down the file.

stdlib.h is another built in library of additional C code. stdlib has lots of useful functions but for the time being we’re using it for the EXIT_FAILURE macro to exit out program correctly if we encounter a problem. We’ll use stdlib more later.

Next we have this code:


void usage();
void create_note(char *note);
void save_note(char *note);
void read_note();

These are function declarations. I’ve mentioned the word function a few times so now is time for an explanation. Functions are basically collections of related code. You’ve used the main function before, which is the collection of code that makes up the main part of our program, these functions are ones we’ve created ourselves and each function has a specific job to do. The names of the functions are pretty self explanatory, but there’s some other parts of the function signature that need to be mentioned. Before the function name you can see the word void. When a function in C finishes the operating system (or the program) needs to know if there’s anything exiting the function, maybe the function worked on some data and is handing that data back. In main we used return 0 to tell Linux that we finished the program without errors, but we can also use return types to send data out of our function back to main to another function for more processing. All functions in C need to have a type, which in our cases is the type void. void means nothing, or empty, meaning our functions aren’t returning anything (yet).

After the function name we have a pair of parenthesis () and a semi colon. All C statements require a semi colon at the end, get used to this as it will cause endless headaches for you when you forget them. The parenthesis allows our function to accept data for processing. Recall above we had the void keyword for the return type and the return is basically data we’re giving back from our function, in the parenthesis we can send data to our function for our function to work on. In our functions, the usage() parenthesis are empty meaning we aren’t accepting any data, but next two, create_note() and save_note() have the words char *note. A char * is essentially C’s way of creating a string. Char is the keyword for character, a character being ‘a’ or ‘b’. A really simplistic explanation for char * is a we’re asking C for a string of chars. There’s more to it, but for now, char * means string. In our functions these are the notes that the user types. The create_note() function requires a string of characters to be passed in to it.

This list of functions is just telling C that they exist and how they look, so C can prepare for their use later on.

Next we have main() which is the function you’ve seen before, it’s the main entry point of our program and where everything happens, but something is different about main().

Previously we just called main like this;

int main()

The int is the return type that the operating system requires, and the parenthesis like mentioned above is the data main expects. The last program didn’t expect data so we left it empty, but this time our main looks like this:

int main(int argc, char *argv[])

Our main accepts two parameters, the first one is an integer, which is just a number, and a char * which is a string, but it’s more than just a string. The square brackets tell C that argv is an array, an array is a list of things, so in our case, char *argv[] means our main function has a list of strings. Both of these, the argc and the *argv[] allow our program to accept user input from the command line. argc is the number of arguments that the user typed, including the program name. For example,

$ ./note read

./note is the name of the program and read is the first argument, so when our program runs argc will be 2.

$ ./note create "This is a note"

This time argc is 3 because we’ve added the string “this is a note” after create. The string in between double quotes counts as a single argument, whereas if we’d typed

$ ./note create This is a note

argc would be 6 and our program wouldn’t know what to do. We need both argc and argv to be able to work with the user input from the command line.

Inside main, we start by processing the command line.


// Check that the user has entered a command
if (argc < 2) {
    usage();
    return EXIT_FAILURE;
}

The first line is a comment. C has two types of comments, in this case // tells C to ignore the rest of the line and is for us as the programmer to remember what our code is doing.

Next is an IF condition. IF allows our program to make decisions and it works like this:

IF whatever is in the parenthesis is true then do whatever is the in curly braces. So this IF is checking whether argc (remember our number of command line arguments) is less than 2. If it’s less than 2 then the user didn’t enter anything after running the program, so we run the usage() function which tells the user how the program works and then we exit the program with a EXIT_FAILURE, which tells the operating system that the program wasn’t run correctly.

If the user did enter something at the command line, meaning argc will be more than 2, so the program moves on to the next section.

The next section actually handles what the user enters and you’ll note we use the strcmp() function from string.h to compare what the user typed with what we need them to type.

First, using another IF, we check if the user entered “help”, and if they did we send them the usage() function again.

Next we have an ELSE IF. This allows our IF expression to handle more than one condition, IF something OTHERWISE something else. Our ELSE IF checks if the user entered “create” and if they do, runs the create_note() function. The create_note() function requires the user to enter some data at the command line, so inside the ELSE IF for “create”, we need to check if they actually did. IF they did, we go about our day and call the function and send it the users note.

Next we have another ELSE IF which checks if the user entered “read”. If that’s the case we run the function to read the text file and print the notes to the screen.

Finally we have ELSE. In and IF expression, ELSE is the last thing we check. IF all the other conditions fail, ELSE is last thing we check before we move on. It goes like this… IF something…. ELSE IF something else….. ELSE IF another thing…. ELSE last thing.

Once all that is said and done, that’s it for main. So the program exits. Most programs will be similar to this in that main is used to call other parts of the program and those other parts, or functions, are what does all the hard work.

After the main function we have all of our other functions. These are all fairly similar to how main() works and should for the most part be self-explanatory.

usage() prints some helpful information to the user.

create_note() for now just accepts the char * (string) that the user entered and then calls the save_note() function. Later on this function will do more, but for now we just want to save the note.

save_note() is a bit more interesting. Again, we need the char * that the user entered for the note. Then we have all this other stuff.


FILE *fp;
fp = fopen("notebook.txt", "a");
fprintf(fp, note);
fprintf(fp, "\n");
fclose(fp);

Basically all this code is just opening a text file and writing our note to the file before closing.

FILE *fp creates a POINTER to a file. A pointer is a C construct that I wont go into depth explaining right now, but basically a pointer POINTS to something. In this case, it points to the file we’re opening.

Next we open the text file. For simplicity we aren’t giving the user the chance to change the file name, we just open a default file using fopen(), passing it the POINTER to our file and a “mode”. The mode we’re using is ‘a’ which means append. When we open files we can either open them to ‘read’, ‘write’, or ‘append’. Reading is obvious, writing is also pretty obvious, and append does the same thing as writing except where writing overwrites the file with new data, append keeps whatever is already in the file and just adds more data to the end. This is what we want, because if we have dozens of notes in our file and we add a new note, we don’t want the program to erase everything. We want to ‘append’.

After opening the file, then we write our data using fprintf. fprintf is similar to the function we’ve used previously printf except it allows us to define WHERE we want to send the data. In this case we’re sending the data to fp which is our file pointer. Another call to fprintf just adds a new line after our note so we don’t have all our notes crammed on one line.

After writing (appending) to the file, we close the file and the function exits.

The last function is the read_note() function and it works similarly to the save_note)() function in that we’re opening a file. This time we open with ‘r’ to read.


void read_note()
{
    FILE *fp;
    char buff[255];
    fp = fopen("notebook.txt", "r");
    while(fgets(buff, 255, (FILE*)fp)) {
        fputs(buff, stdout);
    }
    fclose(fp);
}

First we set up the file pointer. The we need another variable. We haven’t really discussed variables yet, but variables are just buckets to store data. In this case we need a bucket of chars (character) in an array 255 characters long. This is our bucket to store each line of text in our note. We’re storing character data and we can hold up to 255 characters.

Then we open the file like before.

The next section allows us to step through each line of the text file, one at a time and read the data. This is called a LOOP and it’s basically “while” something is happening, keep doing that thing. In our case it’s WHILE there’s still data in the text file. fgets will read each line of the file and when there’s no more lines left, WHILE finishes. If theres 1 line in the file, WHILE will run once, if there’s a thousand lines in the file, WHILE will run a thousand times.

As each line read from the file we print the line to the screen using fputs and when there’s no more lines left the loop ends and we can close the file.

That’s about it. Our program is fairly simple but it has some reasonably complex functionality such as accepting user input, displaying output, and reading and writing to a file.

This program could be much better, but it’s a good first step. I expect you to now go forth and either extend the program and make it your own. You can add more functions to handle additional commands such as deleting a note or allowing the user to create multiple text files. Whatever you want. Or, you can move on and create something new.

Learning C opens up a whole world of software to tinker with. Sure it’s not the easiest language and it requires you to do a lot of extra things that other programming languages do for you, but it also gives you power to manipulate the machine any way you want. With knowledge of C you can write device drivers for new hardware, you can modify the Linux kernel or create a game.

I hope you got something useful from this tutorial. Stay tuned for more coming soon.

Resources

Here’s some of the resources I’ve found most useful in my C journey. I recommend looking at some of these as you progress.

Learn C The Hard Way

https://learncodethehardway.org/

Learn C the hard way is a brilliant book that I highly recommend. More than almost any other website or book, I’ve got them most benefit from this book. The lessons are short and to the point, there’s exercises to test your knowledge and Zed’s explanations are really helpful. I read the free edition years ago when it was in beta, and then I bought the book and video package when it was released as a paid product.

Harvard CS50

https://cs50.harvard.edu/

CS50 is the introduction to Computer Science course from Harvard University and is the actual course that students enrolled in Harvard CS take. You can watch all the videos or enroll in the full course for free and even get your assignments marked. CS50 starts off by teaching you C, data structures, algorithms and moves on to Web programming. This is a fantastic free course and while it takes a while to complete and is a lot of work, it’s worth it.