| Symbol | Description | Example | Result |
|---|---|---|---|
| + | Addition | 10 + 5 | 15 |
| - | Subtraction | 10 - 5 | 5 |
| * | Multiplication | 10 * 5 | 50 |
| / | Division | 10 / 5 | 2 |
| % | Modulus | 10 % 3 | 1 |
| ++ | Pre-increment | x=2; y=++x; | x=3; y=3; |
| ++ | Post-increment | x=2; y=x++; | x=3; y=2; |
| -- | Pre-decrement | x=2; y=--x; | x=1; y=1; |
| -- | Post-decrement | x=2; y=x--; | x=1; y=2; |
Sample code:
#include <stdio.h>
int main() {
// For division to produce decimal results, at least one operand must be floating point
double result = 5/2; // Both operands are integers, truncates to integer
printf("result1 = %lf\n", result);
result = 5.0/2;
printf("result2 = %lf\n", result);
result = 5/2.0;
printf("result3 = %lf\n", result);
int counter = 0;
// Pre-increment
// Increment first, then use value
int value = ++counter;
printf("Pre-increment: value = %d, counter = %d\n", value, counter);
// Post-increment
// Use value first, then increment
counter = 0;
value = counter++;
printf("Post-increment: value = %d, counter = %d\n", value, counter);
return 0;
}
Output:
result1 = 2.000000
result2 = 2.500000
result3 = 2.500000
Pre-increment: value = 1, counter = 1
Post-increment: value = 0, counter = 1
Assignment Operators
| Symbol | Description | Example | Result |
|---|---|---|---|
| = | Assign | x=2; y=3; | x=2; y=3; |
| += | Add and assign | x=0; x+=2; Same as x = x + 2; | x=2; |
| -= | Subtract and assign | x=5; x-=3; Same as x = x - 3; | x=2; |
| *= | Multiply and assign | x=2; x*=2; Same as x = x * 2; | x=4; |
| /= | Divide and assign | x=4; x/=2; Same as x = x / 2; | x=2; |
| %= | Modulus and assign | x=3; x%=2; Same as x = x % 2; | x=1; |
Sample code:
#include <stdio.h>
int main() {
int num = 10;
num += 5;
printf("num = %d\n", num);
return 0;
}
Output:
num = 15
Relational Operators
In C language comparisons, "true" is represented by the number "1" and "false" by the number "0".
| Symbol | Description | Example | Result |
|---|---|---|---|
| == | Equal to | 4 == 3 | 0 |
| != | Not equal to | 4 != 3 | 1 |
| < | Less than | 4 < 3 | 0 |
| > | Greater than | 4 > 3 | 1 |
| <= | Less than or equal | 4 <= 3 | 0 |
| >= | Greater than or equal | 4 >= 1 | 1 |
Sample code:
#include <stdio.h>
int main() {
int left = 10;
int right = 20;
printf("%d\n", left == right);
printf("%d\n", left != right);
printf("%d\n", left > right);
printf("%d\n", left < right);
printf("%d\n", left >= right);
printf("%d\n", left <= right);
return 0;
}
Logical Operators
| Symbol | Description | Example | Result |
|---|---|---|---|
| ! | Logical NOT | !a | If a is false, !a is true; if a is true, !a is false |
| && | Logical AND | a && b | Result is true only if both a and b are true |
| Logical OR |
Sample code:
#include <stdio.h>
int main() {
// && (AND), can be understood as "and"
// Example: Check if both boyfriend and girlfriend meet legal marriage age
int male_age = 25;
int female_age = 21;
int outcome = male_age >= 22 && female_age >= 20;
printf("%d\n", outcome);
// || (OR), can be understood as "or"
// Example: Girlfriend needs gems for Genshin Impact, check if sufficient funds
double wechat_balance = 100;
double alipay_balance = 300;
outcome = wechat_balance >= 398 || alipay_balance >= 398 || wechat_balance+alipay_balance >= 398;
printf("%d\n", outcome);
// ! (NOT), can be understood as "not"
printf("%d\n", !0);
printf("%d\n", !!1);
// Short-circuit behavior
// && left side false, right side not executed
0 && printf("I am on the right\n");
// || left side true, right side not executed
1 || printf("I am on the right\n");
return 0;
}
Bitwise Operators
Common bitwise operators include &, |, ^, ~, >>, <<, representing the following operations:
| Symbol | Description | Example | Result |
|---|---|---|---|
| & | Bitwise AND | 011 & 101 | Both bits must be 1, result is 001 |
| Bitwise OR | 011 | ||
| ^ | Bitwise XOR | 011 ^ 101 | Different bits become 1, result is 110 |
| ~ | Bitwise NOT | ~011 | Inverts all bits, result is 100 |
| << | Left shift | 1010 << 1 | Shifts left, result is 10100 |
| >> | Right shift | 1010 >> 1 | Shifts right, result is 0101 |
Note: NOT and shift operations work on two's complement representation.
& - (AND Operation)
Bitwise AND (&) operation: Compares each bit position, if both are 1, result is 1, otherwise 0;
/**
* Bitwise AND (&): compares bit by bit, result is 1 if both are 1, otherwise 0
* Example:
* 40 & 15 = 8
* 0010 1000
* & 0000 1111
* -------------------
* 0000 1000
*/
printf("40 & 15 = %d\n", 40 & 15);
| - (OR Operation)
Bitwise OR (|) operation: Compares each bit position, if both are 0, result is 0, otherwise 1;
/**
* Bitwise OR (|): compares bit by bit, result is 0 if both are 0, otherwise 1
* Example:
* 40 | 15 = 47
* 0010 1000
* | 0000 1111
* ----------------
* 0010 1111
*/
printf("40 | 15 = %d\n", 40 | 15);
^ - (XOR Operation)
Bitwise XOR operation: Compares each bit position, same values become 0, different values become 1;
/**
* Bitwise XOR: compares bit by bit, same becomes 0, different becomes 1
* Example:
* 40 ^ 15 = 39
* 0010 1000
* ^ 0000 1111
* ------------------
* 0010 0111
*/
printf("40 ^ 15 = %d\n", 40 ^ 15);
~ - (NOT Operation)
Bitwise NOT operation: Two's complement is inverted, then convert the inverted two's complement back to original form;
Note: For unsigned data, even if MSB becomes 1 after inversion, no reverse conversion is needed.
/**
* Bitwise NOT: invert two's complement, then convert back from two's complement to original form.
* 1. Positive number inversion: Since positive numbers have identical original and two's complement forms, the process is simpler.
* Two's complement (original) -> Invert -> Reverse two's complement -> Reverse one's complement (sign bit unchanged) -> Inverted original
* 2. Negative number inversion:
* Original -> One's complement -> Two's complement -> Invert -> Inverted two's complement is original
* Example:
* Original (Two's comp) Inverted Two's comp Reverse two's comp-1 Reverse one's comp
* ~40 = 0010 1000 -> 1101 0111 -> 1101 0110 -> 1010 1001 = -41
*
* Original (Two's comp) Inverted Two's comp Reverse two's comp-1 Reverse one's comp
* ~15 = 0000 1111 -> 1111 0000 -> 1110 1111 -> 1001 0000 = -16
*
* Original One's comp Two's comp Invert
* ~-15 = 1000 1111 -> 1111 0000 -> 1111 0001 -> 0000 1110 = 14
*/
printf("~40 = %d\n", ~40);
printf("~15 = %d\n", ~15);
printf("~-15 = %d\n", ~(-15));
<< - (Left Shift Operator)
Shifts the binary two's complement of the number completely to the left, filling empty positions with 0, discarding overflow bits;
For signed data, if MSB becomes 1 after left shift, reverse conversion is required;
Important notes:
- For unsigned data, even if MSB becomes 1 after left shift, no reverse conversion is needed;
- -128: 1000 0000 special case also doesn't require reverse conversion;
/**
* Examples:
* 40 << 4 = 0010 1000 << 4 = 1000 0000 = -128 (special case, no reverse conversion needed)
* 41 << 4 = 0010 1001 << 4 = 1001 0000 = 1000 1111 = 1111 0000 = -112
* 7 6 5 4 3 2 1 0
* 1 0 0 1 0 0 0 0
*/
int8_t val = 40;
val <<= 4; // val = val << 4;
printf("40 << 4 = %d\n", val);
>> - (Right Shift Operator)
Shifts the binary two's compliment of the number completely to the right, filling empty positions based on the original MSB. If original MSB was 1, fill with 1; if original MSB was 0, fill with 0. This can be simplified as: positive numbers fill 0, negative numbers fill 1;
/*
23: 0001 0111[Original] ---- 0001 0111[One's comp] ---- 0001 0111 [Two's comp]
>> 2
-----------------------------------------------
0000 0101[Two's comp] ---> 5
*/
printf(" 23 >> 2 = %d \n" , 23 >> 2) ;
/*
-23: 1001 0111[Original] ---- 1110 1000[One's comp]---- 1110 1001[Two's comp]
>> 2
-----------------------------------------------
1111 1010[Two's comp] ---> 1111 1001[One's comp]- ----- 1000 0110 [Original]===> -6
*/
printf(" -23 >> 2 = %d \n" , -23 >> 2) ;
Sample code:
#include <stdio.h>
#include <inttypes.h>
int main() {
uint8_t alpha = 3; // 0000 0011
uint8_t beta = 10; // 0000 1010
// Print with 2 characters, pad with 0 if needed
printf("%02x\n", alpha & beta); // 0000 0010, hexadecimal is 02
printf("%02x\n", alpha | beta); // 0000 1011, hexadecimal is 0b
printf("%02x\n", alpha ^ beta); // 0000 1001, hexadecimal is 09
uint8_t gamma = 10; // 0000 1010
uint8_t temporary = ~gamma; // 1111 0101
printf("%02x\n", temporary); // 1111 0101, hexadecimal is f5
printf("%02x\n", gamma << 1); // 0001 0100, hexadecimal is 14
printf("%02x\n", gamma >> 1); // 0000 0101, hexadecimal is 05
return 0;
}
Output:
02
0b
09
f5
14
05
Practical scenarios:
// Set bit 2 of variable alpha to 1, keep other bits unchanged
uint8_t alpha = 0b10110011; // 0xb3;
// Set bits 2 and 6 of variable beta to 1, keep other bits unchanged
uint8_t beta = 0b10110011; // 0xb3;
// Set bit 5 of variable gamma to 0, keep other bits unchanged
uint8_t gamma = 0b10110011; // 0xb3;
// Set bits 0-3 of variable delta to 0, keep other bits unchanged
uint8_t delta = 0b11111111; // 0xff;
// Toggle bit 2 of variable epsilon, keep other bits unchanged
uint8_t epsilon = 0b10110011; // 0xb3;
// Extract bits 8-15 from variable zeta
uint32_t zeta = 0x12345678;
Sample implementation:
#include <stdio.h>
#include <inttypes.h>
int main() {
// Set bit 2 of variable alpha to 1, keep other bits unchanged
uint8_t alpha = 0b10110011; // 0xb3;
alpha |= (1 << 2); // Or x = x | (1 << 2);
printf("%02x\n", alpha); // b7, 10110111
// Set bits 2 and 6 of variable beta to 1, keep other bits unchanged
uint8_t beta = 0b10110011; // 0xb3;
beta |= (1 << 2 | 1 << 6);
printf("%02x\n", beta); // f7,11110111
// Set bit 5 of variable gamma to 0, keep other bits unchanged
uint8_t gamma = 0b10110011; // 0xb3;
gamma &= ~(1 << 5);
printf("%02x\n", gamma); // 93,10010011
// Set bits 0-3 of variable delta to 0, keep other bits unchanged
uint8_t delta = 0b11111111; // 0xff;
delta &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
printf("%02x\n", delta); // f0,11110000
// Toggle bit 2 of variable epsilon, keep other bits unchanged
uint8_t epsilon = 0b10110011; // 0xb3;
epsilon ^= (1 << 2);
printf("%02x\n", epsilon); // b7, 10110111
// Extract bits 8-15 from variable zeta
uint32_t zeta = 0x12345678;
uint32_t temporary = (zeta & 0x0000ff00) >> 8;
printf("%#x\n", temporary);
return 0;
}
Operator Precedence
- Different operators have default precedence levels. With many symbols, it's better to look up when needed rather than memorize.
- When uncertain about precedence, add parentheses to resolve ambiguity.
| Level | Operator | Name/Meaning | Usage | Associativity | Notes |
|---|---|---|---|---|---|
| 1 | [] | Array subscript | array_name[constant_expression] | Left to right | -- |
| () | Parentheses | (expression)/function_name(formal_params) | -- | -- | |
| . | Member access (object) | object.member_name | -- | -- | |
| -> | Member access (pointer) | pointer_object->member_name | -- | -- | |
| 2 | - | Unary minus | -expression | Right to left | Unary operators |
| ~ | Bitwise NOT | ~expression | |||
| ++ | Increment | ++variable_name/variable_name++ | |||
| -- | Decrement | --variable_name/variable_name-- | |||
| *** | Dereference | *pointer_variable | |||
| & | Address-of | &variable_name | |||
| ! | Logical NOT | !expression | |||
| (type) | Type cast | (data_type)expression | |||
| sizeof | Size operator | sizeof(expression) | |||
| 3 | / | Division | expression/expression | Left to right | Binary operators |
| * | Multiplication | expression*expression | |||
| % | Modulus | integer_expression%integer_expression | |||
| 4 | + | Addition | expression+expression | Left to right | Binary operators |
| - | Subtraction | expression-expression | |||
| 5 | << | Left shift | variable<<expression | Left to right | Binary operators |
| >> | Right shift | variable>>expression | |||
| 6 | > | Greater than | expression>expression | Left to right | Binary operators |
| >= | Greater than or equal | expression>=expression | |||
| < | Less than | expression<expression | |||
| <= | Less than or equal | expression<=expression | |||
| 7 | == | Equal to | expression==expression | Left to right | Binary operators |
| != | Not equal to | expression!= expression | |||
| 8 | & | Bitwise AND | expression&expression | Left to right | Binary operators |
| 9 | ^ | Bitwise XOR | expression^expression | Left to right | Binary operators |
| 10 | ** | ** | Bitwise OR | expression | expression |
| 11 | && | Logical AND | expression&&expression | Left to right | Binary operators |
| 12 | ** | ** | Logical OR | expression | |
| 13 | ?: | Condisional | expression1? expression2: expression3 | Right to left | Ternary operator |
| 14 | = | Assignment | variable=expression | Right to left | -- |
| /= | Divide and assign | variable/=expression | -- | -- | |
| *= | Multiply and assign | variable*=expression | -- | -- | |
| %= | Modulo and assign | variable%=expression | -- | -- | |
| += | Add and assign | variable+=expression | -- | -- | |
| -= | Subtract and assign | variable-=expression | -- | -- | |
| <<= | Left shift and assign | variable<<=expression | -- | -- | |
| >>= | Right shift and assign | variable>>=expression | -- | -- | |
| &= | Bitwise AND and assign | variable&=expression | -- | -- | |
| ^= | Bitwise XOR and assign | variable^=expression | -- | -- | |
| ** | =** | Bitwise OR and assign | variable | =expression | |
| 15 | , | Comma | expression,expression,… | Left to right | -- |