The C language provides a number of basic operations. These operations form the basic building blocks of most C expressions. All of the operators in C have a particular precedence order Parentheses can be used to explicitly group operations and force C to evaluate them in a particular another.
The first operation you will ever use is the
assignment operation. The assignment operation, denoted
by =
assigns the value of the expression on the right to
the variable on the left. For example, the expression x = 5
puts the value 5 in the variable x
. You can have any
expression on the right-hand-side so x = 7 * 2
will result
in x
containing the value 14. It is worth noting, however,
that an assignment is also an expression in its own right. When an
assignment expression is evaluated, the result of the expression is that
value that was assigned. Therefore, the expression
x = (y = 7)
will result in both x
and
y
being assigned the value 7.
C provides six basic mathematical operations:
Symbol | Description | Example Expression | Result |
---|---|---|---|
+ |
addition | 7 + 3 |
10 |
- |
subtraction | 7 - 3 |
4 |
- |
negation | -(7 + 3) |
-10 |
* |
multiplication | 7 * 3 |
21 |
/ |
division | 7 / 3 |
2 |
% |
modulus | 7 % 3 |
1 |
Most of these do exactly what you would expect them to do. A little
care is needed when working with division and modulus. For division, if
both operands are integers, it gives you the integer part of the
division and throws away the remainder; if one operand is a
floating-point type, it does a floating-point division. Modulus requires
a little bit of care because (-x) % y
is equivalent to
-(x % y)
which is not the same as in math.
Sometimes it is very useful to add or subtract a number from a
variable and assign it back to the original variable. While we could
write x = x + 7
every time, this can be cumbersome
especially if you have a long variable name. Fortunately, C provides a
nice little shortcut, the +=
family of operators. For each
mathematical operator above as well as each bitwise logical operators
below, there is another operator of the form “#=” where “#” is one of
these operators. So instead of writing x = x + 7
all the
time, you can just write x += 7
. More formally, if
#
is one of these operators, a #= b
is
equivalent to a = a # b
.
In the case of adding or subtracting 1, it gets even better. The C
language provides four operators: --
and ++
that do exactly that. Why did I say four operators and list only two?
Because they are different operators depending on whether you place them
before the variable or after. In either case, the variable gets
incremented (or decremented) by 1. However, the expression
x++
evaluates to the value of x
before it was
incremented and the value of the expression ++x
is the
value of x
after it gets incremented. These operators are
called the post- or pre-increment and post- or pre-decrement operators.
These can be summed up in the following table:
Initial value of x |
Expression | Final value of x |
Value of expression |
---|---|---|---|
5 | ++x |
6 | 6 |
5 | x++ |
6 | 5 |
5 | --x |
4 | 4 |
5 | x-- |
4 | 5 |
In order to allow for conversion from one type to another, C provides
a cast operation. You cast data of one type to another
by putting (type)
in front of the expression. For instance,
if you had two integers x
and y
and you wanted
to do a floating-point division, you could do x / (float)y
which would cast the value of y
to type float
before performing the division. Or, if you had two variables
a
and b
of type short
and you
wanted to multiply them while making sure you don’t get overflow, you
could do a * (int)b
which would guarantee that an
int
multiplication and not a short
multiplication is performed.
When you cast to and from float, C will automatically perform the encoding conversion for you. However, you have to be careful as C does not round and when you convert from a floating-point type to an integer type; the fractional part is simply truncated.
C also provides ways to work with integer data types in a bitwise or logical fashion. The operations described here are bit-wise meaning that it is a boolean operation applied to each bit in the variable independently. The table below describes the four bit-wise logical operation. Note that the expressions below are not technically correct as C would interpret them in base-10. I used binary for ease of reading.
Symbol | Description | Example Expression | Result |
---|---|---|---|
| |
bit-wise OR | 110 | 011 |
111 |
& |
bit-wise AND | 110 & 011 |
010 |
^ |
bit-wise XOR | 110 ^ 011 |
101 |
~ |
bit-wise NOT | !110 |
001 |
Another facility C provides for working with binary data are called the shift operators. These operators shift the data to the right or left a certain number of bits. The table below describes these operators:
Symbol | Description | Example Expression | Result |
---|---|---|---|
<< |
left-shift | 110 << 2 |
11000 |
>> |
right-shift | 110 >> 1 |
11 |
When using the right-shift operator, you have to be mindful of
whether the data being shifted is of a signed or unsigned type. If the
data is unsigned, then a right-shift operation will add zeros to left to
pad the data back to its original size. If the data is signed, the
right-shift operation will pad the data using whatever was in the
left-most bit of the original data. The reason for this is that it
guarantees that negative numbers remain negative when shifted so that
x >> y
is equivalent to x / 2^y
.
C also provides a number of comparison operations and logical operations that are not bit-wise. We will use these much more when we talk about control flow, but we may as well introduce them here. Each of these comparison operations evaluates to zero if the comparison fails and some non-zero value if the comparison succeeds. The exact non-zero value that results from a failed comparison is implementation-dependent.
Symbol | Description | Example Expression | Result |
---|---|---|---|
< |
less than | 5 < 2 |
0 |
<= |
less than or equal to | 2 <= 2 |
nonzero |
> |
greater than | 3 < 3 |
0 |
>= |
greater than or equal to | 3 <= 8 |
nonzero |
== |
equal | 5 == 8 |
0 |
!= |
not equal | 5 != 8 |
nonzero |
Notice that ==
and =
are not the
same. One refers to a comparison operation and the other refers to
assignment. This can get you into quite a bit of trouble by getting
these two mixed up.
I observed above, and will reiterate here, that data types matter when performing comparison operations. An unsigned type will always be greater than or equal to zero even if that same binary value in two’s compliment represents a negative number. Many compilers will also throw warnings if attempt to compare two variables one of which is signed and the other is not.
Along with the above comparison operations, C has three logical operators that do not act in a bit-wise fashion. Instead, they act on the basis of whether the arguments are zero or nonzero. As you would imagine, these are usually used in concert with the comparison operations described above.
Symbol | Description | Example Expression | Result |
---|---|---|---|
|| |
OR | (6 < 5) || (5 < 6) |
nonzreo |
&& |
AND | 0 && 6 |
0 |
! |
NOT | !0 |
nonzero |