Operators
The C language has a large number of operators, over 50 in total, which can be divided into several categories.
Arithmetic operators
Arithmetic operators are used exclusively for arithmetic operations and are mainly the following.
+
: positive value operators (unary operators)-
: negative value operators (monadic operators)+
: addition operators (binary operators)-
: subtraction operators (binary operators)*
: multiplication operator/
: division operators%
: remainder operator
(1) +
, -
+
and -
can be used as either unary or binary operators. The term "unary operator" means that only one operator is needed to perform the operation. The unary operator -
is used to change the plus or minus sign of a value.
int x = -12;
In the example above, -
changes the value 12
to -12
.
The unary operator +
has no effect on positive or negative values and is an operator that can be omitted entirely, but it will not report an error if written.
int x = -12;
int y = +x;
In the example above, the value of the variable y
is still -12
, because +
does not change the positive or negative values.
The binary operators +` and
-` are used to complete addition and subtraction.
int x = 4 + 22;
int y = 61 - 23;
(2) *
The operator `*
is used to complete multiplication.
int num = 5;
printf("%i\n", num * num); // output 25
(3) /
The operator /
is used to complete the division. Note that dividing two integers gives you still an integer.
float x = 6 / 4;
printf("%f\n", x); // output 1.000000
In the above example, even though the variable x
is of type float
(floating point), 6 / 4
gives 1.0
instead of 1.5
. The reason for this is that integer division in C is integer division, which returns only the integer part and discards the fractional part.
If you want to get a floating point result, the two operators must have at least one floating point number, and that's when C does the floating point division.
float x = 6.0 / 4; // or write 6 / 4.0
printf("%f\n", x); // output 1.500000
``''
In the above example, `6.0 / 4` means that a floating point division is performed and the result is `1.5`.
Here is another example.
```c
int score = 5;
score = (score / 20) * 100;
With the above code, you might think that after doing the arithmetic, score
would equal 25
, but in fact score
is equal to 0
. This is because score / 20
is an integer division, which gives an integer value of 0
, so multiplying by 100
gives 0
as well.
To get the desired result, you can change the divisor 20
to 20.0
to make the integer division a floating point division.
score = (score / 20.0) * 100;
(4) %
The operator %
represents the modulo operation, which returns the remainder of two integers divided by each other. This operator can only be used with integers, not floating point numbers.
int x = 6 % 4; // 2
The rule for modulo negative numbers is that the plus or minus sign of the result is determined by the plus or minus sign of the first operator.
11 % -5 // 1
-11 % -5 // -1
-11 % 5 // -1
In the above example, the plus or minus sign of the first operator (11
or -11
) determines the plus or minus sign of the result.
(5) Abbreviated form of the assignment operation
If a variable performs an arithmetic operation on its own value, C provides a shorthand form that allows the assignment operator and the arithmetic operator to be combined into a single operator.
+=
-=
*=
/=
%=
Here are some examples.
i += 3; // equivalent to i = i + 3
i -= 8; // equivalent to i = i - 8
i *= 9; // equivalent to i = i * 9
i /= 2; // equivalent to i = i / 2
i %= 5; // equivalent to i = i % 5
Self-incrementing operators, self-subtracting operators
C provides two operators that operate on variables themselves + 1
and - 1
.
++
: the self-incrementing operator-
: the self-subtracting operator
i++; // equivalent to i = i + 1
i--; // equivalent to i = i - 1
These two operators are placed before or after a variable and the result is not the same. ++var
and --var
perform a self-increasing or self-subtracting operation before returning the value of var
after the operation; var++
and var--
return the value of var
before the operation before the self-increasing or self-subtracting operation.
int i = 42;
int j;
j = (i++ + 10);
// i: 43
// j: 52
j = (++i + 10)
// i: 44
// j: 54
The difference in position of the self-incrementing operator in the above example will result in the variable j
getting a different value. Such a writing style is prone to unexpected results, and to eliminate surprises, the following writing style can be used instead.
/* Write I */
j = (i + 10);
i++;
/* Write II */
i++;
j = (i + 10);
In the above example, the self-incrementing operation on the variable i
and the return value are two separate steps, which makes it less likely that errors will occur and improves the readability of the code.
Relational operators
C expressions used for comparison are called "relational expressions" and the operators used in them are called "relational operators". There are six main ones, as follows.
>
greater-than operator<
Less than operator>=
greater than or equal to operator<=
Less than or equal to operator==
Equality operators! =
Unequal operators
Here are some examples.
a == b;
a ! = b;
a < b;
a > b;
a <= b;
a >= b;
Relational expressions usually return 0
or 1
, indicating truth or falsity; in C, 0
indicates falsity and all non-zero values indicate truth. For example, 20 > 12
returns 1
and 12 > 20
returns 0
.
Relational expressions are commonly used in if
or while
constructs.
if (x == 3) {
printf("x is 3.\n");
}
Note that the equality operator ==
and the assignment operator =
are two different operators and should not be confused. Sometimes the following code may be accidentally written, and it will run, but it is easy to get unexpected results.
if (x = 3) ...
In the example above, the original meaning is x == 3
, but it is accidentally written as x = 3
. This equation means that the variable x
is assigned the value 3
, which returns 3
, so the if
judgement is always true.
To prevent this error, some programmers prefer to write the variable to the right of the equal sign.
if (3 == x) ...
In this case, if ==
is mistakenly written as =
, the compiler will report an error.
/* Report an error */
if (3 = x) ...
Another mistake to avoid is that multiple relational operators should not be used concatenated.
i < j < k
In the example above, two less-than operators are used in succession. This is a legal expression and will not report an error, but it usually does not achieve the desired result, i.e. it is not guaranteed that the value of the variable j
is between i
and k
. Because the relational operators are calculated from left to right, the following expression is actually executed.
(i < j) < k
In the above equation, i < j
returns either 0
or 1
, so it is ultimately 0
or 1
that is compared with the variable k
. If you want to determine whether the value of the variable j
is between i
and k
, you should use the following writeup.
i < j && j < k
Logical operators
Logical operators provide logical judgements for constructing more complex expressions, and there are three main operators as follows.
!
: the no operator (changes the truth or falsity of a single expression).&&
: the with operator (true if both sides of the expression are true, false otherwise).||
: the or operator (true if at least one expression on both sides is true, false otherwise).
Here is an example of the with operator.
if (x < 10 && y > 20)
printf("Doing something!\n");
In the above example, x < 10
and y > 20
are true only if both are true, and x < 10 && y > 20
is true.
The following is an example of the whether operator.
if (!(x < 12))
printf("x is not less than 12\n");
In the above example, since the no operator !
has a higher precedence than <
, the parentheses must be used in order to perform the no operation on the expression x < 12
. Of course, a reasonable way to write this would be if (x >= 12)
, and this is just for the sake of example.
For logical operators, any non-zero value indicates true, and a zero value indicates pseudo. For example, 5 || 0
would return 1
and 5 && 0
would return 0
.
The logical operator also has the feature that it always evaluates the expression on the left before the expression on the right, and this order is guaranteed. If the left-hand expression satisfies the conditions of the logical operator, the right-hand expression is no longer evaluated. This is called a "short circuit".
if (number ! = 0 && 12/number == 2)
In the above example, if the expression to the left of &&
(number ! = 0
) is false, i.e. number
is equal to 0
, the expression on the right (12/number == 2
) is not executed. Because the left-hand expression returns 0
at that point, the whole &&
expression must be pseudo, and it simply returns 0
and no longer executes the right-hand expression.
Since logical operators are executed in the left-hand order before the right-hand order, the following code is problematic.
while ((x++ < 10) && (x + y < 20))
In the above example, the value of the variable x
has changed after the execution of the left-hand expression. By the time the right-hand expression is executed, it is being calculated with the new value, which is usually not the original intent.
Bitwise operators
The C language provides a number of bitwise operators for manipulating binary bits (bits).
(1) Inverse operator ~
The inverse operator ~
is a unary operator used to turn each binary bit into its opposite value, i.e. 0
becomes 1
and 1
becomes 0
.
// Returns 01101100
~ 10010011
In the above example, ~
takes the inverse of each binary bit and you get a new value.
Note that the ~
operator does not change the value of the variable, it just returns a new value.
(2) With the operator &
The with operator &
compares each of the binary bits of two values and returns a new value. When both binary bits are 1
, 1
is returned, otherwise 0
is returned.
// returns 00010001
10010011 & 00111101
In the above example, two eight-bit binary numbers are compared bit by bit, returning a new value.
The with operator &
can be combined with the assignment operator =
and abbreviated to &=
.
int val = 3;
val = val & 0377;
// abbreviated as
val &= 0377;
(3) The or operator |
The or operator |
compares each of the binary bits of two values and returns a new value. Two binary bits return 1
as long as one is 1
(containing the case where both are 1
), otherwise it returns 0
.
// returns 10111111
10010011 | 00111101
The or operator |
can be combined with the assignment operator =
and abbreviated to |=
.
int val = 3;
val = val | 0377;
// abbreviated as
val |= 0377;
(4) The difference or operator ^
The difference operator ^
compares each binary bit of two values and returns a new value. If one and only one of the two binary bits is 1
, 1
is returned, otherwise 0
is returned.
// returns 10101110
10010011 ^ 00111101
The difference or operator ^
can be combined with the assignment operator =
and abbreviated to ^=
.
int val = 3;
val = val ^ 0377;
// abbreviated as
val ^= 0377;
(5) The left shift operator <<
The left shift operator <<
shifts each bit of the left-hand operator by a specified number of bits to the left, with the trailing empty space filled with 0
.
// 1000101000
10001010 << 2
In the above example, each binary bit of 10001010
is shifted two bits to the left.
The left shift operator is equivalent to multiplying the operator by a specified power of 2. For example, a left shift of 2 bits is equivalent to multiplying by 4 (2 to the power of 2).
The left-shift operator <<
can be combined with the assignment operator =
and abbreviated to <<=
.
int val = 1;
val = val << 2;
// abbreviated as
val <<= 2;
(6) The right-shift operator >>
The right-shift operator >>
shifts each bit of the left-hand operator by a specified number of bits to the right; any value that cannot be accommodated at the end is discarded, and the empty space at the head is filled with 0
.
// Returns 00100010
10001010 >> 2
In the above example, each binary bit of 10001010
is shifted two places to the right. The lowest two 10
bits are discarded and the two extra bits in the head are complemented by 0
, so you end up with 00100010
.
Note that the right-shift operator is best used only for unsigned integers and not for negative numbers. This is because different systems have different approaches to how they handle the sign bit of a negative number after a right shift, and may give different results.
The right-shift operator is equivalent to dividing the operator by a specified power of 2. For example, a right-shift of 2 bits is equivalent to dividing by 4 (2 to the power of 2).
The right-shift operator >>
can be combined with the assignment operator =
and abbreviated to >>=
.
int val = 1;
val = val >> 2;
// abbreviated as
val >>= 2;
The comma operator
The comma operator is used to write multiple expressions together, running each expression in turn from left to right.
x = 10, y = 20;
In the example above, there are two expressions (x = 10
and y = 20
) and the comma allows them to be placed inside the same statement.
The comma operator returns the value of the last expression as the value of the whole statement.
int x;
x = 1, 2, 3;
In the above example, the comma has a lower priority than the assignment operator, so the assignment operation is performed first, then the comma operation, and the variable x
equals 1
.
Operation priority
Priority means that if an expression contains more than one operator, which operator should be executed first. The priority of the various operators is not the same.
3 + 4 * 5;
In the above example, the expression 3 + 4 * 5
contains both an addition operator (+
) and a multiplication operator (*
). Since multiplication has a higher priority than addition, 4 * 5
will be computed first, rather than 3 + 4
.
If two operators have the same priority, the order of execution is determined by whether the operators are left-combined, or right-combined. Most operators are left-bound (executed from left to right), and a few are right-bound (executed from right to left), such as the assignment operator (=
).
5 * 6 / 2;
In the example above, *
and /
have the same priority; they are both left-binding operators and so are executed from left to right, computing 5 * 6
first and then 6 / 2
.
The order of precedence of operators is complex. Here is the order of precedence for some of the operators (in descending order of precedence)
- Round brackets (
()
) - Self-incrementing operators (
++
), self-subtracting operators (--
) - Unary operators (
+
and-
) - Multiplication (
*
), division (/
) - Addition (
+
), subtraction (-
) - Relational operators (
<
,>
, etc.) - Assignment operators (
=
)
Since the parentheses have the highest priority, you can use them to change the priority of other operators.
int x = (3 + 4) * 5;
In the example above, addition is performed before multiplication because of the addition of the parentheses.
It is not necessary to remember the precedence of all operators completely. The solution is to use more round brackets to prevent unexpected situations and to help improve the readability of the code.