Getting up and running with C can be hard for the beginner, using IDE's can be confusing especially when trying to include dependencies. Here I'll document my learning path that was most beneficial to me when learning how to use C.

While learning C, I believe it is beneficial to your learning process to use some form of linux machine, a solid linux distribution is manjaro linux, if you have an old spare laptop, I recommend installing a lightweight linux distro on it.

IMPORTANT: before you start this guide, make sure you have a C reference, for that I recommend ansi c book which you can find online, or learncpp website, if you want a paper copy, use this tool

Compiled Vs Interpreted

A processor on your computer runs commands in the form of machine code, which is a special language that is most efficient and each command it processes does some atomic operation, and is not readable by a human

If we go one step up we would have something called assembly code, which is a language which allows us to write these atomic operations in a human readable format but is quite a low level language.

Finally going another layer up, we have high level programming languages like C, Java, Python, ... which allow us to focus on larger ideas. With these layers in place, we should be able to realize that in order for any program to do anything at some point it has to get to the CPU, meaning it has to be in machine code.

One way of converting a program into machine code would be to first take the program file then convert it into an executable containing the already converted machine code, or we could run the program file directly translating each line into the proper machine code and simultaneously running it.

An easy comparison would be taking a document written in french, then having someone come and translate the entire thing into english and then you reading it versus them live-translating the document from french to english. In the former case, it would be faster to read the document once already translated, as compared to the live translation, since you're dependent on the time it takes for the translator to convert between the languages.

A language in which you write programs that are first turned into machine code as an executable are called compiled languages, and ones that do the translation every time the program is run is called an interpreted language. C is a compiled language.

How to Compile

Now that we know that C is a compiled language, we need a method to compile our programs, on linux an easy one is gcc, so for example if you had a program which is valid you could run gcc example.c to create the executable a.out.


Compiling by hand for simple projects is fine, but over time as your projects get more complicated you'll want to use systems that can build your project easily, on unix systems one of the most well known is called Make, look up "Make Explained" on your search engine of choice, and you should be able to learn about what it does and how it works. I also recommend reading what the difference is betwen make and gcc

Cross Platform

Make is great when you're at a linux command line, but sometimes you might not be, and potentially if your software gains enough steam you'll have developers with different preferences on how they interact with the code.

To facilitate this situation, the program Cmake exists, read this (query "cmake vs makefiles" on google.com) to understand the difference. Another good starting point is how cmake is used (query "is cmake a compiler" on google.com).

I'll assume you've read that and you're on board with cmake, now visit cmake's official website and use it as a reference to get started.


To debug your programs in the command line I recommend gdb, in order to use gdb you must compile your program in a debug mode. Use man g++ and find the -g option, this embeds debug symbols into the executable making it easy to debug. With cmake you can pass the option -D CMAKE_BUILD_TYPE=Debug

For more detailed debugging focused on memory, we'll quote paleozogt from stack exchange:

Sometimes the crash itself isn't the real cause of the problem-- perhaps the memory got smashed at an earlier point but it took a while for the corruption to show itself. Check out valgrind, which has lots of checks for pointer problems (including array bounds checking). It'll tell you where the problem starts, not just the line where the crash occurs.

compiling for different operating systems

When you create an executable it's built for your hardware and os, so that an executable for your computer will probably not work on some other random computer.

If you want to build for windows then

An Object file is the compiled file itself. There is no difference between the two. An executable file is formed by linking the Object files. Object file contains low level instructions which can be understood by the CPU. That is why it is also called machine code. This low level machine code is the binary representation of the instructions which you can also write directly using assembly language and then process the assembly language code (represented in English) into machine language (represented in Hex) using an assembler. Here's a typical high level flow for this process for code in High Level Language such as C --> goes through pre-processor --> to give optimized code, still in C --> goes through compiler --> to give assembly code --> goes through an assembler --> to give code in machine language which is stored in OBJECT FILES --> goes through Linker --> to get an executable file. This flow can have some variations for example most compilers can directly generate the machine language code, without going through an assembler. Similarly, they can do the pre-processing for you. Still, it is nice to break up the constituents for a better understanding. An object file is the real output from the compilation phase. It's mostly machine code, but has info that allows a linker to see what symbols are in it as well as symbols it requires in order to work. (For reference, "symbols" are basically names of global objects, functions, etc.) A linker takes all these object files and combines them to form one executable (assuming that it can, i.e.: that there aren't any duplicate or undefined symbols). A lot of compilers will do this for you (read: they run the linker on their own) if you don't tell them to "just compile" using command-line options. (-c is a common "just compile; don't link" option.) A .o file is the result of compiling a single compilation unit (essentially a source-code file, with associated header files) while a .a file is one or more .o files packaged up as a library. I believe an .a file is an archive that can contain multiple object files. .o files are objects. They are the output of the compiler and input to the linker/librarian. .a files are archives. They are groups of objects or static libraries and are also input into the linker. Additional Content I didn't notice the "examples" part of your question. Generally you will be using a makefile to generate static libraries. AR = ar CC = gcc objects := hello.o world.o libby.a: $(objects) $(AR) rcu $@ $(objects) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ This will compile hello.c and world.c into objects and then archive them into library. Depending on the platform, you may also need to run a utility called ranlib to generate the table of contents on the archive. An interesting side note: .a files are technically archive files and not libraries. They are analogous to zip files without compression though they use a much older file format. The table of contents generated by utilities like ranlib is what makes an archive a library. Java archive files (.jar) are similar in that they are zip files that have some special directory structures created by the Java archiver.

comments section