Operators in C Language
C language includes a wide variety of operators, more than 50 in total, which can be categorized into several types.
Arithmetic Operators
Arithmetic operators are used for mathematical operations. The main ones are as follows:
+
: Unary plus (positive sign)-
: Unary minus (negative sign)+
: Addition (binary operator)-
: Subtraction (binary operator)*
: Multiplication/
: Division%
: Modulus (remainder of division)
- Unary
+
and-
:
The+
and-
can act as unary operators, requiring only one operand. Unary-
negates a value:
1 | int x = -12; |
In this example, -
changes 12 to -12.
Unary +
has no effect and can be omitted without any issue:
1 | int x = -12; |
- Binary
+
and-
:
These operators are used for addition and subtraction:
1 | int x = 4 + 22; // x is 26 |
- Multiplication
*
:
Used for multiplication:
1 | int num = 5; |
- Division
/
:
Used for division. Note that division between two integers results in an integer:
1 | float x = 6 / 4; |
In this example, even though x
is of type float
, the division 6 / 4
results in 1.0
, not 1.5
. This is because integer division in C truncates the decimal part, returning only the integer portion.
To get a floating-point result, at least one operand must be a float. For example:
1 | float x = 6.0 / 4; // Or 6 / 4.0 |
Here, 6.0 / 4
performs floating-point division, yielding 1.5
.
Consider this example:
1 | int score = 5; |
You might expect score
to be 25
, but it will actually be 0
. This is because score / 20
performs integer division, resulting in 0
, and multiplying this by 100
also gives 0
.
To get the expected result, change 20
to 20.0
to use floating-point division:
1 | score = (score / 20.0) * 100; |
- Modulus
%
:
The%
operator represents the modulo operation, which returns the remainder of dividing two integers. This operator can only be used with integers, not floating-point numbers. For example:
1 | int x = 6 % 4; // Result is 2 |
The rules for modulo with negative numbers dictate that the sign of the result is determined by the sign of the first operand(the sign of the result follows the first operand):
1 | 11 % -5 // Result is 1 |
In these examples, the sign of the result (whether it is positive or negative) matches the sign of the first operand (11 or -11).
- Compound Assignment Operators
C provides shorthand forms for performing arithmetic operations on a variable and assigning the result back to the same variable. These compound assignment operators combine the assignment operator with an arithmetic operator:
+=
-=
*=
/=
%=
Here are some examples:
1 | i += 3; // Equivalent to i = i + 3 |
These shorthand notations make code more concise and readable.
Increment and Decrement Operators
C has two operators to increase or decrease a variable by 1:
++
: Increment operator--
: Decrement operator
Example:
1 | int i = 42; |
The position of these operators affects their behavior. If placed before a variable (++i
), it increments first and then returns the value. If placed after (i++
), it returns the current value before incrementing.
Consider this example:
1 | int i = 42; |
The position of the increment operator affects the value assigned to j
. To avoid confusion and improve code clarity, separate the increment from the expression:
1 | /* Option 1 */ |
This way, the increment operation and the value assignment are distinct, reducing potential errors and enhancing readability.
Relational Operators
In C, relational operators are used to compare values and are crucial for conditional statements. Here are the six primary relational operators:
>
: Greater than<
: Less than>=
: Greater than or equal to<=
: Less than or equal to==
: Equal to!=
: Not equal to
Examples:
1 | a == b; // Checks if a is equal to b |
Relational expressions return 0 (false) or 1 (true). For instance, 20 > 12
returns 1, while 12 > 20
returns 0.
These expressions are commonly used in if
or while
statements:
1 | if (x == 3) { |
Note: The equality operator ==
is different from the assignment operator =
. Avoid mistakes like this:
1 | if (x = 3) ... |
This assigns 3 to x
and always evaluates to true. Instead, write:
1 | if (3 == x) ... |
This way, if you mistakenly use =
instead of ==
, the compiler will catch the error.
Also, avoid chaining relational operators like this:
1 | i < j < k |
This does not check if j
is between i
and k
. Instead, use:
1 | i < j && j < k |
Logical Operators
Logical operators are used for logical comparisons and constructing more complex expressions. Here are the three main logical operators:
!
: Logical NOT (reverses the truth value of a single expression).&&
: Logical AND (true if both expressions are true, otherwise false).||
: Logical OR (true if at least one of the expressions is true, otherwise false).
For example with the AND operator:
1 | if (x < 10 && y > 20) |
In this case, x < 10 && y > 20
is true only if both x < 10
and y > 20
are true.
For the NOT operator:
1 | if (!(x < 12)) |
Here, !
has higher precedence than <
, so parentheses are needed to negate x < 12
. A more straightforward way to write this would be if (x >= 12)
.
Logical operators treat any non-zero value as true and zero as false. For instance, 5 || 0
returns 1, and 5 && 0
returns 0.
A key feature of logical operators is short-circuit evaluation: the left expression is evaluated first, and if it determines the result, the right expression is not evaluated. For example:
1 | if (number != 0 && 12/number == 2) |
If number
is 0, 12/number
will not be evaluated, preventing a divide-by-zero error.
When using logical operators, be cautious of changing variables within the expressions:
1 | while ((x++ < 10) && (x + y < 20)) |
Here, x
is incremented in the left expression, potentially affecting the right expression in unexpected ways.
Bitwise Operators
C provides several bitwise operators to manipulate binary digits (bits). Here’s a rundown of each:
Bitwise NOT Operator (~
)
The bitwise NOT operator (~
) is a unary operator that inverts each bit of its operand, changing 0s to 1s and 1s to 0s.
1 | // Returns 01101100 |
In the example above, applying ~
to each bit of 10010011
results in a new value. Note that the ~
operator does not alter the original value but returns a new one.
Bitwise AND Operator (&)
The bitwise AND operator (&
) compares each bit of two operands. It returns 1 if both bits are 1; otherwise, it returns 0.
1 | // Returns 00010001 |
Here, 10010011
and 00111101
are compared bit by bit, resulting in a new value. The &
operator can be combined with the assignment operator =
, written as &=
.
1 | int val = 3; |
Bitwise OR Operator (|
)
The bitwise OR operator (|
) compares each bit of two operands and returns 1 if at least one of the bits is 1. If both bits are 0, it returns 0.
1 | // Returns 10111111 |
In this example, 10010011
and 00111101
are compared bit by bit, resulting in a new value. The |
operator can also be combined with the assignment operator =
, written as |=
.
1 | int val = 3; |
Bitwise XOR Operator (^
)
The bitwise XOR operator (^
) compares each bit of two operands. It returns 1 if only one of the bits is 1; if both are 0 or both are 1, it returns 0.
1 | // Returns 10101110 |
The ^
operator can be combined with the assignment operator =
, written as ^=
.
1 | int val = 3; |
Left Shift Operator (<<
)
The left shift operator (<<
) shifts the bits of its left operand to the left by a specified number of positions. The empty positions on the right are filled with 0s.
1 | // Returns 1000101000 |
Here, each bit in 10001010
is shifted two positions to the left. The left shift operator is equivalent to multiplying the operand by 2 raised to the specified power (e.g., shifting left by 2 positions is the same as multiplying by 4).
The left shift operator can be combined with the assignment operator =
, written as <<=
.
1 | int val = 1; |
Right Shift Operator (>>
)
The right shift operator (>>
) shifts the bits of its left operand to the right by a specified number of positions. The bits that fall off are discarded, and the empty positions on the left are filled with 0s.
1 | // Returns 00100010 |
In this example, each bit in 10001010
is shifted two positions to the right. The two lowest bits (10
) are discarded, and the leftmost bits are filled with 0s. Note that right shifting is best used with unsigned integers to avoid issues with how different systems handle the sign bit.
The right shift operator is equivalent to dividing the operand by 2 raised to the specified power (e.g., shifting right by 2 positions is the same as dividing by 4).
The right shift operator can be combined with the assignment operator =
, written as >>=
.
1 | int val = 1; |
Comma Operator
The comma operator allows multiple expressions to be written in a single statement, executing them from left to right.
1 | x = 10, y = 20; |
In this example, the comma operator lets you include both x = 10
and y = 20
in one statement.
The comma operator returns the value of the last expression, which is the value of the whole statement.
1 | int x; |
Here, the comma operator inside the parentheses results in x
being assigned the value 3
, the last expression.
Operator Precedence
Operator precedence determines the order of operations in expressions with multiple operators. Different operators have different precedence levels.
1 | 3 + 4 * 5; |
In this expression, multiplication (*
) has higher precedence than addition (+
), so 4 * 5
is calculated first.
If operators have the same precedence, their associativity decides the order. Most operators are left-associative (evaluated from left to right), while a few, like the assignment operator (=
), are right-associative (evaluated from right to left).
1 | 5 * 6 / 2; |
Since both *
and /
have the same precedence and are left-associative, the expression is evaluated as 5 * 6
first, then 30 / 2
.
The precedence of operators is complex. Here’s a simplified list from highest to lowest:
- Parentheses
()
- Increment (
++
), Decrement (--
) - Unary operators (
+
,-
) - Multiplication (
*
), Division (/
) - Addition (
+
), Subtraction (-
) - Relational operators (
<
,>
, etc.) - Assignment operators (
=
)
Using parentheses can override default precedence and improve code clarity:
1 | int x = (3 + 4) * 5; |
In this case, the parentheses ensure that addition is performed before multiplication.
Remembering all operator precedences is unnecessary. Use parentheses to clarify and ensure your code behaves as expected.