C
Note: These notes are a work-in-progress and still need to be processed, re-organized, and cleaned up. Currently the information here is just a simple brain dump of everything I've read and learned recently. Over time I will go through these and make them more concise and understandable. These notes were written using [[Using Obsidian|Obsidian]] with a lot of plugins, and the formatting displayed by those plugins may not appear correctly on the hosted version of these notes (where you're reading them now). These formatting issues will also be fixed when I go though and process these notes.
C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs between 1972 and 1973. It is one of the most widely-used languages and was standardized by ANSI in 1989. This document will mainly refer to C17, as most of the source documentation does. See [[#References]] for a list of sources.
The Basics
In this section we will cover a bit about programming in general.
Imperative Programming
C programs have the computer do specific tasks by giving orders
The orders are similar to how humans would express things in the imperative tense in spoken languages
So, the term imperative programming was coined for this way of organizing computer programs
[!tip] Takeaway C is an imperative programming language.
The code displayed below will be our example for explaining various aspects of C. The end result of it is outputting 5 lines of text on the console.
Code Sample: print_squares.c
And here is the output of the program in the console:
We will reference this code frequently throughout the beginning of this article. For now, just remember that it orders the computer to print text to the console.
Compiling and Running
The code in [[#Code Sample print_squares.c|print_squares.c]] is just another piece of text that exists on your hard drive. To actually run, it needs to be translated into a form that can be understood by your computer. A special program called a compiler does this for us. It translates the C code into binary code (also called an executable)), which your computer can read and understand. This process is called compiling.
[!tip] Takeaway C is a compiled programming language.
Which compiler you use will depend on the platform on which you will be running your program. This is because the binary code is platform dependent. Basically, different types of machines speak different languages. Your computer understands a different binary format than your phone or your camera. One of the main reasons C exists is that it provides a level of abstraction for all the different machine-specific languages. This abstraction is usually called an assembler.
[!tip] Takeaway A correct C program is portable between different platforms.
Some platforms claim to be "C" but do not conform to the latest standards. There are also platforms that accept "incorrect" programs or provide extensions to the C standard that are not widely portable. Because of this, running and testing a program on a single platform will not always guarantee portability. By portable, we mean that whenever you run the program, its behavior should be the same.
It's the compiler's job to ensure that your program will run correctly on various platforms after being translated.
The process of compiling your program will differ based on your compiler. Here we will use gcc
for our examples. You may want to check which compiler your system uses and look up the documentation for it.
An example of compiling a C program using gcc:
Here the compiler takes the file print_squares.c
, translates it into binary code, and outputs that binary code to a file called print_squares
.
When compiling programs, you may get warnings from the compiler. If that happens, pay attention to what they're saying and attempt to fix them as best you can. The compiler is there to help you, and therefore you should listen to it when it has something to say.
[!tip] Takeaway A C program should compile cleanly without warnings.
Note that while the above statement is a good goal to have, it is not always possible. There are many widely-used C programs that ship with compiler warnings. A better way to phrase this statement is that a C program should compile with as few warnings as possible.
The Structure of A Program
Grammar
A C program is composed of different types of text elements that are assembled together
Special Words
Represent concepts and features in C that cannot be changed
Examples:
#include
,int
,void
, etc.
Punctuation
C uses several types of punctuation to structure program text
Five different kinds of brackets: {}, (), [], /* */, <>
Used to group certain parts of the program together
Should always come in pairs
Two different separators or terminators: comma and semicolon
Commas separate things
Semicolons terminate things
Comments
Denoted by // and /* */
Ignored by the compiler
Perfect place to explain and document code
Literals
Fixed values that are a part of the program
Identifiers
Names given to different entities in a program
They may refer to:
Data objects / variables
Type aliases
Functions
Constants
Functions
Operators
Declarations
Before we can use an identifier, it must be declared
Declaring specifies what the identifier is supposed to represent
Different from keywords; keywords are predefined by the language and must not be declared or redefined
[!tip] Takeaway All identifiers in a program have to be declared.
Some examples of identifiers in [[#Code Sample print_squares.c|print_squares.c]] are
arr
andi
Variables are named items that allow us to store values
Declarations are bound to the scope in which they appear.
Definitions
Identifiers only specify the kind of object it refers to, not what it is
Definitions are what identifiers identify
[!tip] Takeaway Declarations specify identifiers, while definitions specify objects.
Initialization is an augmented declaration where an initial value is provided. For example:
size_t i = 0
[!tip] Takeaway An object is defined at the same time it is initialized.
Each object or function must have exactly one definition
Statements
Statements are instructions that tell the compiler what to do with identifiers
An Introduction to C
This section will provide information for writing good, modern C programs. By "good", we mean modern and portable.
Control Flow
C has five conditional control statements:
if
,for
,do
,while
, andswitch
if
introduces a conditional execution depending on a Boolean expressionfor
,do
, andwhile
are different forms of iterationsswitch
is a multiple selection based on an integer value
Conditional Execution (if
statements)
if
statements)An if
statement looks something like this:
In this example we compare i
to the value 25. If it is larger than 25, then the value of j
is set to i - 25
. The expression i > 25
is called the controlling expression and the part in { ... }
is called the dependent block or dependent statement.
Note that there is only one part inside the parentheses, which determined whether the dependent statement or block is run once or not at all. This is different from a for
loop, which we will discuss later.
Another, more general form of the if
statement is as follows:
This has a second dependent statement or block that is executed if the controlling condition is not fulfilled. This is done by adding the keyword else
that separates the two statements or blocks.
The if (...) ... else ...
is called a selection statement. It selects one of the two code paths according to the contents of ( ... )
. The general form of this is
Here, condition
can be a number of things ranging from simple to very complex nested expressions. The simplest example can be seen below.
In this example, the condition that determines whether printf
runs is just i
. A numerical value by itself can be interpreted as a condition. The text will only be printed when the value of i
is not 0.
There are two simple rules for the evaluation of a numerical condition:
The value
0
represents logical false.Any value different from
0
represents logical true.
The operators ==
and !=
allow for testing equality and inequality. a == b
is true if the value of a
is equal to the value of b
; a != b
is false if a
is equal to b
, and true otherwise.
Iterations
for
Loops
for
LoopsIn the example sample_if.c
we encountered the for
statement. The general form of this statement is
Usually, clause1
is an assignment expression or variable definition. It sets the initial value for the iteration domain. condition2
tests whether the iteration should continue. Then, expression3
updates the iteration value used in clause1
. It is performed at the end of each iteration.
Here is an example:
In this example, the for
counts up from 0 to 5, exclusive. When the value of i
is greater than or equal to 5, the loop stops.
Note that the iteration variable is named i
. It is common to use the letters "i" and "j" as names for this variable.
[!tip] Takeaway It is customary to use the name
i
for your iteration variable. A loop nested inside the initial loop will use the namej
, the loop nested inside that will usek
, and so on. Note this this is only done when using "throwaway" variables.
while
Loops
while
LoopsThe two other iterative statements in C are while
and do
. Their basic forms are:
Here is an example of a normal while
loop:
In this example, do_something
will execute repeatedly until the value of i
is less than or equal to 3
.
The do-while
loop is very similar, except that it checks the condition after the dependent block:
This means that if the condition evaluates to false
, the while
loop will not run at all while the do
loop will run one before terminating. Basically, if you want to run a while
loop that always executes at least once, this is the way to do it.
break
And continue
break
And continue
All three iteration statements become even more flexible with break
and continue
statements. A break
statement stops the loop immediately:
Here, once the loop executes the break
statement, it immediately terminates. The same can be done for for
loops.
The continue
statement is similar to break
in that it skips the iteration of the rest of the dependent block. However, instead of terminating the loop, it instead re-evaluates the condition and continues from the start of the dependent block if the condition is true. Basically, it skips the rest of the current iteration of the loop and moves to the next iteration.
Multiple Selection
The final control statement in C is the switch
statement. It is a selection statement and is mainly used when cascades of if-else
blocks would be too tedious:
In this case, we have a choice that is more complex than a false-true
decision and that can have several outcomes. We can simplify this by using a switch
statement:
Here, we select one of the puts
calls according to the value of arg
. We provide specific cases for the characters m
, r
, j
, and c
and a fallback case labeled default
. The default case is triggered if arg
doesn't match any of the case
values.
Syntactically, a switch
is as simple as
and its semantics are straightforward: case
and default
serve as jump targets, which means that the program's execution will jump to them when a certain condition is met. If we hit a break
statement, the whole switch
terminates.
There are two things to note about switch
statements:
case
values must be integer constant expressionscase
labels must not jump beyond a variable definition
References
Websites
Books
Articles
Last updated