Operators are basically just instructions to DO stuff. The ones we’ve seen so far are math operators:

//Assignment operator
int i = 5;
 
//Another assignment
i = 7;
 
//Assignment and addition operators
int x = i + i + i;

The operators above have operands, the things they DO WORK on. Let’s look at some common operators!

Incrementing an integer variable

int i = 5; //assignment
 
//All of these lines do the same thing:
 
i = i + 1; //Line 1
i += 1; //Line 2
++i; //Line 3
i++; //Line 4

Result

Each of the lines of code above do the same thing. On each line, the value of the integer variable i is increased by 1. After all of this code has run, the value of i will be 9.

Decrementing an integer variable

int i = 10; //assignment
 
//All of these lines do the same thing:
 
i = i - 1; //Line 1
i -= 1; //Line 2
--i; //Line 3
i--;

Result

Each of the lines of code above do the same thing. On each line, the value of the integer variable i is decreased by 1. After all of this code has run, the value of i will be 6.

Literal Values

You might have noticed that for some code we can choose to use numbers, OR we can choose to use variables. In the code below, the %d format specifier means we need to insert an integer value. The value of an integer variable would work, or we could instead just use a number we already have in mind:

int x = 8;
 
//Printing out the value of an integer variable
printf("%d", x);
 
//Printing out an integer literal
printf("%d", 437);

When we use “raw values” in our code, they are called literals, or literal values. Below are examples of literals of different data types:

//int literal values
2
89
0
500025748
 
//float literal values
-472.45516f
2000.0000f
0.000008733f
 
//char literal values
'A'
'b'
'$'
' '
'\n'
 
//bool literal values (there's only two)
true
false

Expressions

When we write a line of code in C, we’re most often creating something called an expression. Understanding expressions is key to understanding what a line of code is actually accomplishing.

Expressions are just operators and operands. For example, look at the following lines of code:

 
int Health = 10;
 
Health = Health + 20;
 

OK, it’s relatively easy to see what’s going on here. But what about this:

int a, b, c, d, e;
//imagine that I assgined values to these 
//variables before the next line of code
e = a * b * (c + d);
 
int i, j, k, u, v, w, x, y, z;
//imagine that I assgined values to these 
//variables before the next line of code
z = ((x + y) + (w * v * u)) / (j - k + i);

Those last couple lines are pretty long! How do we translate this C code into a running program? Recall that after we are done writing C code, it must be converted into machine code that can be run by our computer. To do this, we COMPILE our code; it is handed to the compiler, which attempts to translate our C code into machine code, represented by an actual program (.exe).

The compiler breaks down our lines of code like so:

  • Identify all tokens
  • Sort tokens into operators and operands
  • Convert operands into other types (if possible)
  • Resolve each operator according to precedence

What does this all mean? We’ll walk through the steps together!

Tokenization

When the compiler encounters an expression, it is broken down into individual tokens. Tokens are the smallest elements of a programming language, used to represent things in that language.

Semicolons denote the end of expressions! We’ve seen those before! The compiler also tries to figure out if a token is valid, like a variable name or operator.

Operators and Operands

The compiler sorts the tokens into operators and operands! Variables and literals are operands (things that are WORKED UPON) and things like ’+’, ’-’, and ’=’ are operators (things that DO WORK).

Operands: data Operators: manipulate data (instructions!)

There are unary, binary, and ternary operators (which operate on one, two, or three operands respectively). The vast majority of our operators will be binary, but we’ll use a few unary operators.

Type Conversion

The compiler may need to convert some values into different types. Remember how we talked about what kind of work is attractive to our CPU? It’s good at performing operations on things of the same type! So, the compiler is usually smart enough to know about converting types, when needed:

int applePie = 3;
float decimals = 0.14159f;
 
//Value stored in myPi will be 3.14159f
float myPi = applePie + decimals;

In the example above, what is happening? The compiler knows that to complete the operation of adding applePie and decimals together, they must be the same type. For that one operation, the compiler converts the value of applePie into a float, so it can be added to the float value of decimals. The compiler is doing an implicit conversion; it knows how to do this conversion without any input from the programmer.

But sometimes the compiler isn’t so smart:

 
float x = 1234.898f;
int a = 0;
 
a = x;

What happens when we do this? The compiler will either warn us that what we’re doing is unsafe, or it will give us an error stating that it can’t compile our code. But why?

If we want to assign the value in x to the variable a, that value must be converted from a float into an integer. But since integers only store whole numbers, we’d be losing information! The decimal places would be cut off! And the compiler understands that probably isn’t the outcome we’re going for.

We can prevent issues by doing the following:

float x =  1234.898f;
int a = 0;
 
//Value of a is now 1234
a = (int)x;

This is called CASTING. We are casting the float variable x to an integer; we want to interpret its value as an integer for the purpose of assigning to a. This explicit conversion allows us to get around the compiler being scared about losing decimal information when converting our float number into an integer.

You won’t use this practice terribly often yourself, but it’s important to know that it is possible, for certain situations.

Operator Precedence

Remember order of operations, from highschool math? Yeah… it’s like that!

3 + 4 * 2 //11
3 + (4 * 2) //Also 11
(3 + 4) * 2 //14
 
-4 + 7 //3
-(4 + 7) //-11

TLDR; Follow the PEMDAS way like in highschool math and you should be fine: Parentheses, Exponent, (Multiplication, Division), (Addition, Subtraction)

Remember also (from math) that operators have associativity; if operators have the same precedence, then they’re resolved based on associativity, usually LEFT to RIGHT.

2 * 6 / 4 //The resulting value is 3