C++ Fundamentals II: Operators, Expressions, Type Conversion & Swapping

3
0

C++ Fundamentals - II

This part of C++ fundamentals focuses mainly on expressions, operators, type conversion, bitwise operations, and swapping values. These topics are small individually, but they appear everywhere in programming. Once they become familiar, writing logic becomes much smoother because most decisions, calculations, comparisons, and updates in a program are built using them.

Expressions in C++

An expression is a combination of values, variable names, and operators that C++ evaluates to produce a value or cause a side effect, such as changing the value stored in a variable.

Examples of expressions include:

  • a + b => An arithmetic expression that adds the values stored in a and b and produces their sum.

  • marks >= 40 => A relational expression that checks whether the value stored in marks is greater than or equal to 40. It evaluates to either true or false.

  • count = count + 1 => An assignment expression that adds 1 to the current value of count and stores the result back in count, thereby changing its value. This change is called a side effect.

Side Effect

Not every expression changes stored data. For example, a + b simply calculates a result; it does not modify a or b.

int a = 10;
int b = 20;
int sum = a + b;

Here, a + b produces 30, and that value is stored in sum. The values of a and b remain unchanged.

But in the following expression: count = count + 1;

The value of count is updated. If count was 5, it becomes 6. This update is the side effect.

Operands and Operators

In an expression such as: a + b

+ is the operator, while a and b are operands. The operator tells C++ what operation to perform, and the operands are the values or variables on which the operation is performed.

Arithmetic Operators

Arithmetic operators are used for mathematical calculations.

  • + performs addition.

  • - performs subtraction.

  • * performs multiplication.

  • / performs division.

  • % gives the remainder after division.

Remainder Operator

The remainder operator, also called modulo, gives the remainder after integer division.

When 13 is divided by 2, the quotient is 6 and the remainder is 1. So: 13 % 2 = 1

The modulo operator is useful in many problems, such as checking whether a number is even or odd.

int number = 13;
if (number % 2 == 0) {
cout << "Even";
} else {
cout << "Odd";
}

If a number gives remainder 0 when divided by 2, it is even. Otherwise, it is odd.

Integer Division

Integer division is a common source of mistakes. If both operands are integers, the result is also an integer, even if the mathematical answer has a decimal part.

int total = 56;
int num = 6;
double average = total / num;
cout << average;
// Output: 9.0

Mathematically, 56 / 6 is approximately 9.3333. But in this code, both total and num are integers, so C++ performs integer division first. The result becomes 9, and then 9 is stored in the double variable average.

Storing the result in a double is not enough. At least one operand must be a floating-point value before the division happens.

int total = 56;
int num = 6;
double average = static_cast<double>(total) / num;
cout << average;
// Output: 9.3333

Now total is converted to double before division, so the result is a decimal value.

Type Conversion in C++

Type conversion means converting a value from one data type to another. For example, converting an int value into a double value is a type conversion.

int x = 10;
double y = x;

Here, x stores the integer value 10. When it is assigned to y, C++ automatically converts it to 10.0. This is allowed because a double can comfortably store an integer value.

Implicit Type Conversion

Implicit conversion happens automatically. The programmer does not explicitly request it; C++ performs it when needed.

int x = 10;
double y = x;

In this example, C++ automatically converts x from int to double.

Another example:

int total = 56;
double num = 6;
double average = total / num;
cout << average;

Here, total is an integer and num is a double. Since one operand is a double, C++ converts the integer operand to double and performs floating-point division. The result is approximately 9.33333.

Explicit Type Conversion

Explicit conversion happens when the programmer forces a conversion. In modern C++, static_cast is the preferred form.

int total = 56;
int num = 6;
double average = static_cast<double>(total) / num;
cout << average;

static_cast<double>(total) tells C++ to convert total into a double. Once one operand becomes double, the division produces a decimal result.

There is also an older C-style cast syntax:

int total = 56;
int num = 6;
double average = (double)total / num;
cout << average;

This also converts total to double, but static_cast is clearer and safer in modern C++ style.

Conversion Can Lose Information

Converting an int to a double preserves the value of ordinary small integers, representing them with a decimal part when appropriate. However, converting a double to an int removes the fractional part rather than rounding the number, which can result in a loss of information.

int x = 10;
double y = x; // y becomes 10.0
double price = 10.56;
int value = price; // Fractional part is discarded; value becomes 10
cout << value; // Prints: 10

Character and Integer Conversion

Characters are internally associated with integer values. In the commonly used ASCII representation, uppercase and lowercase English letters have fixed numeric codes.

  • 'A' has ASCII value 65.

  • 'B' has ASCII value 66.

  • 'Z' has ASCII value 90.

  • 'a' has ASCII value 97.

  • 'b' has ASCII value 98.

  • 'z' has ASCII value 122.

Character to Integer

C++ can convert a character to its corresponding integer character code. When a character is assigned to an int variable, its corresponding integer value is stored.

char ch1 = 'A';
int value1 = ch1;
cout << value1; // Prints: 65
char ch2 = 'a';
int value2 = ch2;
cout << value2; // Prints: 97

Integer to Character

The reverse is also possible. If an integer corresponds to a valid character code, it can be converted to char.

int value1 = 65;
char ch1 = value1;
cout << ch1; // Prints: A
int value2 = 97;
char ch2 = value2;
cout << ch2; // Prints: a

If a number is outside the meaningful range for the character set being used, the output may look strange or unreadable. For basic ASCII, values are often discussed in the range 0 to 255, but exact behavior can depend on the system and character representation.

Assignment Operators

The assignment operator stores a value into a variable.

// This stores 90 in score.
int score = 90;

The left side of assignment must be a modifiable object, usually a variable.

score = 90; // valid
90 = score; // invalid

A constant value cannot appear on the left side because it cannot be modified.

Updating a Value

score = score + 5;

This means:

  • Read the current value of score.

  • Add 5 to it.

  • Store the new value back into score.

If score was 90, it becomes 95.

Compound Assignment

Compound assignment combines an operation with assignment. For example:

score += 5; // Adds 5 to score
// Equivalent to:
score = score + 5;

Other compound assignment operators work similarly:

score -= 5; // score = score - 5
score *= 2; // score = score * 2
score /= 3; // score = score / 3
score %= 4; // score = score % 4

Compound assignment is shorter and often clearer when the same variable is being updated.

Increment and Decrement Operators

The increment operator ++ increases a value by 1. The decrement operator -- decreases a value by 1.

Without Updating the Original Variable

int x = 5;
int y = x + 1;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 6
x = 5
*/

y gets x + 1, but x itself remains unchanged.

Updating the Original Variable

int x = 5;
x = x + 1;
int y = x;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 6
x = 6
*/

This can be shortened using compound assignment: x += 1;

It can be shortened further using increment: ++x;

Prefix Increment

In prefix increment, the operator appears before the variable.

int x = 5;
int y = ++x;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 6
x = 6
*/

Here, x is increased first. Then the updated value is assigned to y.

Prefix Decrement

int x = 5;
int y = --x;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 4
x = 4
*/

x is decreased first, and then the updated value is assigned to y.

Postfix Increment

In postfix increment, the operator appears after the variable.

int x = 5;
int y = x++;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 5
x = 6
*/

The old value of x is assigned to y first. After that, x is incremented.

Postfix Decrement

int x = 5;
int y = x++;
cout << "y = " << y << "\n";
cout << "x = " << x << "\n";
/*
Output:
y = 5
x = 6
*/

The old value is used first, then the decrement happens.

Avoid Dense Increment Expressions

Expressions that modify the same variable multiple times in one line are hard to read and should be avoided.

// Avoid writing expressions like this
x++ + ++x + x++;

Even if such expressions appear clever, they make code difficult to reason about. Readability matters more than cleverness. If a value needs to be updated multiple times, write the steps clearly.

Relational Operators

Relational operators compare values and produce either true or false.

  • == checks equality.

  • != checks inequality.

  • < checks less than.

  • > checks greater than.

  • <= checks less than or equal to.

  • >= checks greater than or equal to.

Equality Check

int x = 5;
int y = 5;
bool result = (x == y);
cout << result; // Prints: 1

In C++, true is commonly printed as 1 and false as 0.

If y is changed to 6:

int x = 5;
int y = 6;
bool result = (x == y);
cout << result; // Prints: 0

Assignment vs Equality

= and == are different.

// Assigns 5 to x.
x = 5;
// Checks whether x is equal to 5.
x == 5;

Logical Operators

Logical operators combine or modify boolean conditions.

  • && means logical AND.

  • || means logical OR.

  • ! means logical NOT.

Logical AND

&& returns true only when both conditions are true.

marks >= 0 && marks <= 100

This checks whether marks lies between 0 and 100, inclusive. Both conditions must be true.

Logical OR

|| returns true if at least one condition is true.

day == 6 || day == 7

This can be used to check whether day is either 6 or 7. If either condition is true, the whole expression is true.

Logical NOT

! reverses a boolean value.

bool result = true;
cout << !result; // Prints: 0

Since result is true, !result becomes false.

Short-Circuiting

Logical operators do not always evaluate both sides. This behavior is called short-circuiting.

Short-Circuiting with AND

For the expression, A && B, to be true, both A and B must be true. If C++ evaluates A and finds it false, the result is already guaranteed to be false. There is no need to evaluate B.

bool result = false && someOtherCondition;

The second condition is skipped because false && anything is always false.

Short-Circuiting with OR

For the expression, A || B, to be true, at least one condition must be true. If C++ evaluates A and finds it true, the result is already guaranteed to be true. There is no need to evaluate B.

bool result = true || someOtherCondition;

The second condition is skipped because true || anything is always true.

Short-circuiting can save unnecessary computation. More importantly, it is often used to write safe checks.

if (denominator != 0 && numerator / denominator > 5) {
cout << "Valid calculation";
}

If denominator != 0 is false, the division is never performed, so division by zero is avoided.

Operator Precedence and Parentheses

Operator precedence decides which operation happens first when an expression has multiple operators. This is similar to mathematics.

int result = 2 + 3 * 4;
cout << result; // Prints: 14
// Calculation:
// 3 * 4 = 12
// 2 + 12 = 14

Multiplication has higher precedence than addition, so 3 * 4 happens first. So result becomes 14.

int result = (2 + 3) * 4;
cout << result; // Prints: 14
// Calculation:
// 2 + 3 = 5
// 5 * 4 = 20

Parentheses can change the order. Now 2 + 3 happens first. So result becomes 20.

Even when precedence rules are known, large expressions should be avoided if they hurt readability. It is better to write a few clear lines than one dense line that is hard to understand.

Conditional Operator

The conditional operator is also called the ternary operator. It is used to choose between two values based on a condition.

condition ? value_if_true : value_if_false

It is useful for simple decisions.

Problem: Print the Larger of Two Numbers

Given two unique numbers, print the larger one. Since the two numbers are guaranteed to be different, there is no equal case to handle.

Using If-Else

#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
if (a > b) {
cout << a;
} else {
cout << b;
}
return 0;
}
/*
Input: 10 4
Output: 10
Input: 10 40
Output: 40
*/

Using the Conditional Operator

#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
int result = (a > b) ? a : b;
cout << result;
return 0;
}

If a > b is true, a is chosen. Otherwise, b is chosen.

For a = 10 and b = 40, the condition 10 > 40 is false, so b is selected and 40 is stored in result.

Printing Directly

The value chosen by the conditional operator can be printed directly.

cout << ((a > b) ? a : b);

This is fine for simple cases. Avoid putting complex logic inside a ternary expression because it quickly becomes unreadable.

Bitwise Operators

Bitwise operators work directly on the binary representation of numbers. This section only introduces the operators; deeper bit manipulation is studied later as a separate topic.

The operators discussed are:

  • & bitwise AND

  • | bitwise OR

  • ^ bitwise XOR

Do not confuse & with &&. A single & is bitwise AND. Double && is logical AND used in conditions.

Binary Representation

The machine stores numbers using 0 and 1. For example, the decimal number 6 is written as 110 in binary.

To convert 6 to binary, repeatedly divide by 2 and note the remainders:

6 / 2 = 3 remainder 0
3 / 2 = 1 remainder 1
1 / 2 = 0 remainder 1

Read the remainders from bottom to top. So, 6 = 110 in binary.

Although we write 6 as 110 for simplicity, an int commonly uses 32 bits. So internally, it is stored with leading zeros i.e. 00000000000000000000000000000110

For a long long, which commonly uses 64 bits, the same value has even more leading zeros.

Bitwise AND

Bitwise AND produces 1 at a bit position only when both bits are 1.

6 = 110
3 = 011
Now compare bit by bit:
110
& 011
-----
010

At the rightmost bit, 0 & 1 = 0. At the middle bit, 1 & 1 = 1. At the left bit, 1 & 0 = 0.

The result is: 010

Convert it back to decimal:

0 * 2^0 = 0
1 * 2^1 = 2
0 * 2^2 = 0
Total = 0 + 2 + 0 = 2

Therefore, 6 & 3 = 2

Bitwise OR

Bitwise OR produces 1 at a bit position if at least one of the bits is 1.

6 = 110
3 = 011
Compare bit by bit
110
| 011
-----
111

Convert 111 to decimal:

1 * 2^0 = 1
1 * 2^1 = 2
1 * 2^2 = 4
Total = 1 + 2 + 4 = 7

Therefore, 6 | 3 = 7

Bitwise XOR

Bitwise XOR produces 1 when the number of 1s at that bit position is odd. For two bits, this means it gives 1 when the bits are different and 0 when they are the same.

6 = 110
3 = 011
Compare bit by bit
110
^ 011
-----
101

At the rightmost bit, there is one 1, so the result bit is 1. At the middle bit, there are two 1s, so the result bit is 0. At the left bit, there is one 1, so the result bit is 1.

Convert 101 to decimal:

1 * 2^0 = 1
0 * 2^1 = 0
1 * 2^2 = 4
Total = 1 + 0 + 4 = 5

Therefore, 6 ^ 3 = 5

Swapping Two Numbers

Swapping means exchanging the values of two variables. If a = 5 and b = 7, then after swapping, a = 7 and b = 5.

Incorrect Swap Attempt

A common first attempt is:

a = b;
b = a;

This does not work. Suppose a = 5 and b = 7.

After the first line: a = b;

a becomes 7. The original value 5 is lost.

Then the second line: b = a;

assigns the current value of a, which is already 7, into b. Both variables become 7. No swap happens.

Swapping Using a Temporary Variable

To swap safely, preserve one value before overwriting it.

#include <iostream>
using namespace std;
int main() {
int a = 5;
int b = 7;
int temp = a;
a = b;
b = temp;
cout << "a = " << a << "\n";
cout << "b = " << b << "\n";
return 0;
}

Output:

a = 7
b = 5

The steps are:

  • temp = a preserves the original value of a.

  • a = b assigns the value of b to a.

  • b = temp assigns the original value of a to b.

Swapping Without a Temporary Variable

Swapping can also be done using arithmetic operations.

#include <iostream>
using namespace std;
int main() {
int a = 5;
int b = 7;
a = a + b;
b = a - b;
a = a - b;
cout << "a = " << a << "\n";
cout << "b = " << b << "\n";
return 0;
}

Step-by-step:

  • Initial: a = 5, b = 7

  • Step 1: a = a + b => a = 5 + 7 = 12, b = 7

  • Step 2: b = a - b => b = 12 - 7 = 5

  • Step 3: a = a - b => a = 12 - 5 = 7

  • Final result: a = 7, b = 5

Overflow Risk in Arithmetic Swap

The arithmetic method has a risk. If a and b are large, a + b may exceed the maximum value that the integer type can store. This causes overflow and can make the program incorrect.

int a = 2147483647;
int b = 1;
a = a + b; // overflow risk

Since 2147483647 is commonly the maximum value for a signed 32-bit int, adding 1 cannot fit in the same type.

Because of this, the temporary variable method is clearer and safer for beginners.

Key Takeaways

  • An expression combines values, variable names, and operators to produce a value or cause a side effect.

  • Integer division returns an integer when both operands are integers.

  • Type conversion can be implicit or explicit.

  • Converting from double to int can lose the decimal part.

  • Characters can be converted to their integer codes and vice versa.

  • Assignment updates values; compound assignment shortens common updates.

  • Prefix and postfix increment/decrement differ in when the updated value is used.

  • Relational operators produce boolean results.

  • Logical operators combine boolean conditions and support short-circuiting.

  • Parentheses should be used to make expressions clear.

  • The ternary operator is useful for simple choices, but complex logic should remain readable.

  • Bitwise operators work on binary representations of numbers.

  • Swapping with a temporary variable is simple, readable, and safe for beginners.

C++

Read Similar Blogs

Comments0