Now that we have a basic understanding of the raw data types, let’s look at the operators. You are likely already familiar with many of the C operators (unless you are still in kindergarten). The most basic are:

* multiply

/ divide

% remainder

+ add

- subtract

= assign value

These are straightforward operators with three quirks: first, the order of operations is
imperative; second, divide does not do any rounding; third, the operators may
cause an overflow depending on the data type. The code below illustrates these
operators and their quirks as well as introduces formatted number printing using printf().

The output of the above program is:

The program output illustrates how these operators work.

The order of operations is first to multiply then to add: (5 * 10) + 10 is 60

The order of operations again is to first multiply then add: 5+(10 * 10) is 105

Because 5 divided by 10 is 0 with a remainder of 5 the value assigned to z is 0. No rounding is performed.

Because z is an unsigned 8-bit number (0 to 255), assigning five minus six to it causes the number to overflow to 255 (see the diagram below).

z is assigned the remainder of 13 divided by two. This operation is very handy for identifying even and odd numbers.

Lines one and two both illustrate the order of operations. The operations are
generally executed from left to right with multiply and divide always happening
before add and subtract. The assign operator (=) has the lowest precedence
and is executed last. Parentheses always override the default order of operations;
for example, z=(x+y) times 10 will first add x and y then multiply the result
by 10 and assign the value to z.

Binary Operators

In addition to the easily recognizable operators, C has addition operators that are mostly based on binary number representation. In order to understand these operations, a cursory knowledge of binary numbers is required.

A binary number uses just two symbols (0 and 1) to represent a value. This is why it is known as a base-2 numbering system. The classical number system most people are familiar with uses ten symbols (0 to 9) known as base-10 or decimal numbering. To understand binary, we need to take a closer look at the decimal system.

When we count in the decimal system, we go through each of the ten symbols
(usually skipping zero). When we hit the last symbol (the number 9),
we go back to zero and add another column. Each symbol in the new column
represents a number ten times greater than the last column. The counting below illustrates this:

0, 1, 2 … 9, 10 (add a column and restart the symbols), … 98, 99, 100 (add a column and restart), 998, 999, 1000 (add a column and restart)
If we recall learning to count, we learned the first column is known as the one’s column, then the ten’s column, etc. If we apply this logic to binary, where there are only two symbols, we count like this:

0, 1, 10 (add a column and restart), 11, 100 (add a column and start over), 101, 110, 111, 1000 (add a column and restart)
Instead of the one’s, ten’s and hundred’s column, each new column is just two times greater yieding the one’s, two’s, four’s, eight’s, and sixteen’s column. The table below shows binary numbers from 0 to 15.

When looking at binary numbers, you can convert to decimal by adding the value of the column for the columns with a one. Take 12 for example. It is written 1100 in binary. The column values are 8-4-2-1. There are ones in the 8 column and the 4 column and 8+4 is equal to 12. For the binary number 7 (111), we add 4+2+1 to get 7.

Armed with an understanding of binary numbers, the binary operators in C should come easily. The operators include “shift”, “and”, “or”, “xor”, and “not”. A shift comes in left and right varieties while the others can be either bit-wise or logical. The following code illustrates shifting.

The above program produces the following output.

Looking at the output above:

Five (0101) shifted to the right drops the one’s column to get binary 2 (010). Shifting one bit to the right is the same thing as dividing by 2. This is analogous to decimal numbers. If you shift a decimal number to the right 1, you are dividing by 10. Likewise, shifting right 2 bits divides by 4 and shifting right 3 bits divides by 8 and so on.
Two (010) becomes 16 (010000) when shifted to the left 3 bits. Since shift to the right is the same as dividing, shifting one bit to the left multiplies by 2; two bits multiplies by 4; three bits multiplies by 8; and so on. In this example, 2 shifted left 3 bits is equal to 2 times 8 or 16.
If a bit is shifted out of the data type, it is dropped. 128 (10000000) shifted left one becomes zero because the bit shifts out of the 8 bits because z is a uint8_t. If z were a uint16_t, the new value for z would be 256.

The next operators are the bit-wise and logical “or”, “and”, “xor”, and “not”.
The bit-wise “or” operator is represented by | (it is the one on the same key as ).
If any of the inputs are one, the output is one. The bit-wise “and” operator is
represented by the & symbol. For each bit, if both input are one, then the output
is one. If any of the input is zero, the output is zero. For “xor”, represented by ^,
the output is one if exactly one input is one. A truth table is typically used to
document the output of binary bit-wise operators. The following is the truth table
for & where x and y are inputs and z is the output.

The bit-wise “not” operator is denoted in C using the ~. It only has one argument. The output changes all the zeros to ones and vice-versa.

p The logical versions of “and”, “or”, and “not” assume the inputs are either zero or non-zero and output one or zero accordingly. The symbols are &&,

(two, consecutive vertical lines), and ! respectively. The code example below illustratres how to use these operators.

The output of the above program is:

This is a bitwise “and” of 0b00000101 (5) and 0b00000111 (7). Since bits 0 and 2 are one in both input values, they are both one in the output value of 0b00000101 (5).

For the bitwise “or” of 0b00000101 (5) as well as 0b00000111 (7), the output is 0b00000111 because these bits are set in either of the inputs.

For the logical “and” operation, the output is always zero or one. The inputs are considered non-zero or zero. A non-zero input acts as a one where a zero input acts as a zero. # # # Since x is zero, the output is zero.

The logical “or” treats the inputs in the same manner as the logical “and” but outputs a one because y is a non-zero input.

For logical “not”, the output is zero if the input is non-zero; the opposite is also
true.

Comparison Operators

We are going back to some basic operators with which you are already familiar.
These operators (like logical “and”/”or”) only output a one or a zero. They are
typically used in program flow control, which is covered later, rather than just
doing math. Here are the operators:

== is one if the two arguments are equal; zero otherwise

> is one if the left argument is greater than the right

>= is one if the left argument is greater than or equal to the right

< is one if the left argument is less than the right

<= is one if the left argument is less than or equal to the right

Note A single equal sign
(=) is an assignment operator and a double equal sign (==) is a comparison operator.

The following program demonstrates how these work.

The output of the program is:

These operators should be pretty straightforward. There is one thing to remember.
It is bad practice to use == with a floating point value. It is better to bound
the range using something like

Notice how the logical
“and” is used in conjuction with the other operators. Combining operators is
really where these become powerful tools to programmers.

Combining Operators

When combining operators, the order of execution is critical for getting the
desired result. The order of operations is mentioned briefly above in the
*\/ and +- section and is equally important for the binary and comparison operators.

When combining operators, things can get confusing when trying to deduce the order of operations. It is good programming practice to use copious amount of parentheses to make things clear both to you and to future maintainers of your code. The program below illustrates both good and bad use of parentheses when combining operators.

Shortcut Operators

The C language provides a number of shorthand ways to do some operations. One of the most common is ++ which increments a variable.

The code above demonstrates both post-increment and pre-increment short cuts. It outputs:

x is 0

x is now 2

The x++ statement increments x after its value is passed to the printf() function while ++x increments the variable before it is passed to printf().

Here is the full list of shortcuts plus their equivalents:

Take Away

The C language includes many mathematical operators for doing basic arithmetic, comparisons,
and binary operations. The order of operations is critical in C for correctly evaluating
expressions. Sometimes understanding this order can be difficult when skimming through code,
but adding parentheses can help to make code easier to read and avoid unintended evaluation orders.

Now that we have a basic understanding of the raw data types, let’s look at the operators. You are likely already familiar with many of the C operators (unless you are still in kindergarten). The most basic are: