Intro to C


What is C?

You know what C is. But just in case you don't, C is widely considered the grandfather of all modern programming languages, and was the first "high level" language. In other posts, I use "high level" to refer to scripting/garbage collected languages that abstract away specific implementations like memory management, arrays, pointers, etc, and low level to refer to mainly one of Rust, C, C++, or Assembly. However, high level in this case means a programming language itself and not just a direct abstraction of CPU instructions like assembly.

 

C directly inspired the syntax of other languages such as Java, and indirectly inspired the syntax of pretty much every other language.

 

Is C hard?

The perceived difficulty of C isn't due to it's syntax, which is quite barebones, as C is incredibly minimalistic by design. It takes 20 minutes to learn the syntax, and maybe less than a few days to a week to learn it's libraries. Instead, the difficult part is that C forces you to think about pointers, memory addresses, memory allocation/deallocation, and how low level processes actually work.

 

The Actual Guide Part

Basic Program Structure

As with every compiled language, all C programs need an entry point. This is the main function in your C file, and should return an int. The returned value is your exit code for your program.

(0 = success, 1 = error, 2 = invalid use of command, etc.)

So a base file should look something like this:

 

Defining Variables

For now, we won't focus on manual memory management, and instead let C manage the allocation and deallocation of our variables. C has a few built in datatypes, although notably it does not have booleans or strings.

As you can see, defining variables is pretty simple. An int on most modern systems is 4 bytes long, or a 32 bit integer, which means it's range is from -(231) to 231 - 1, or -2 147 483 648 to 2 147 483 647 (the -1 is because we start counting positive numbers at 0, not 1). C also supports unsigned integers, in which the bit used to represent the sign is instead used to also hold the number's capacity. This means the number can only be positive (no sign, or unsigned), but effectively doubles the positive range, making 0 to 4 294 967 295 the range an unsigned int can hold.

 

Other notable types are char and unsigned char (-128 to 127 and 0 to 255 respectively) to hold character ASCII values, short, which is a 2 byte (8 bit) integer. and long, which is a 64 bit integer (upper bound of 263 - 1, limited to 32 bits on 32 bit systems).

 

Since some of these vary by platform, you can print sizeof(type) to get the actual size in bytes of the given type on that specific platform.

 

Here is a demonstration of using these types:

C also includes floating point numbers, such as float, a 4 byte floating number, double, an 8 byte floating number, and long double, a 10 byte floating number. I won't dive too deep into how floats work, as this post is already getting quite technical for an introduction post, but the main takeaway is floats can have decimals while integers cannot. Floats can hold bigger numbers than integers, but at the cost of precision.

 

If you don't want to read about the technical details of floats, skip this part.

Essentially, a 32 bit integer stores 31 bits for numbers, and 1 bit for the sign, right? Since we start the count from 0, this makes it's upper limit 231 - 1, or roughly 2.147 billion. Conversely, a float stores 24 bits for numbers and 8 bits for exponents, making it's upper limit about 238, or around 100 undecillion. This means it can store up to 24 bit numbers (around 16.7 million) with precision, then starts losing precision after that mark.

(In practice, these values may differ.)

I promise, that's enough technical stuff for now.

 

Standard Libraries and Printing

C has a few included standard libraries you can include by putting #include <libname> at the very top of the file. The one this section is going to focus on is stdio, specifically the printf() function.

 

To start, we include the stdio library that has printf() (.h signifies a header file, which are kind of like modules)

Now printf can be used in one of two ways. You can pass a string literal to print out directly, like printf("Hello world!"), or use special format strings. There are different format strings for different value types, like %s for strings, %d for any decimal number, %p for memory addresses, etc.

With format strings, you can define a layout for your string, and pass substitutions as additional arguments. Both ways to use printf are shown below:

Note that we include \n at the end of every line ourselves. This is because printf doesn't automatically move to a new line after every call, which means you can have multiple calls to form a complex line of text.

 

Arrays

Arrays in C are fixed size as expected, and can be defined and indexed like so:

Fun fact, in C, arrays are basically just pointers, and indexing an array like arr[n] is syntax sugar for getting the value at the memory address of (arr + n). More on this in the pointers article.