Variables, Data Types, and Operators
Welcome back! Today we are going to talk about the absolute core building blocks of any C++ program: Variables, Data Types, and Operators.
Without these three, your code wouldn’t be able to store data, make decisions, or calculate anything. Think of it this way:
- Variables: Labeled containers where you store your data.
- Data Types: The label on the container telling C++ what kind of data is inside (whole numbers, text, decimals).
- Operators: The operations you perform on those containers (like adding them together, comparing them, etc.).
A Quick Real-World Analogy
Think about the calculator app on your phone:
- When you type in
5and10, the calculator stores those numbers in its memory (inside variables). - The calculator knows these are whole numbers, not words (that’s the data type).
- When you press the
+button, it uses an addition operator to calculate15.
Let’s dive into how this works in C++!
Understanding Variables
What are Variables?
In C++, a variable is a named portion of the computer’s memory. When you declare a variable, you tell the computer to reserve a small slot of RAM for your program, and you give that slot a name (an identifier) so you can retrieve it later.
Rules for Declaring Variables
C++ is pretty strict about variable names. When naming your variables, keep these rules in mind:
- Variable names must start with a letter or an underscore
_(e.g.,age,_score). - They can contain letters, numbers, and underscores, but no spaces or special characters (
@,#,$, etc.). - Names are case-sensitive (e.g.,
Age,AGE, andageare three completely different variables). - You cannot use C++ reserved keywords (like
int,double, orreturn).
Syntax for Declaring Variables
To create a variable, you must tell the compiler what type of data it holds, give it a name, and optionally assign it an initial value:
data_type variable_name = value;
Let’s look at a concrete example:
int age = 25;
int(Data Type): Tells C++ that this variable holds a whole number integer.age(Variable Name): The name we use to refer to this RAM storage slot.25(Value): The actual data we want to store.
Declaring Multiple Variables at Once
If you need to declare several variables of the exact same type, you don’t have to write a new line for each. You can group them on a single line separated by commas:
#include <iostream>
int main() {
int a = 10, b = 20, c = 30;
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
return 0;
}
Output:
a: 10, b: 20, c: 30
Scope of Variables
The scope of a variable determines where in your code that variable can be accessed. C++ has two main types of scopes:
1. Local Variables
A variable declared inside a function or a block of code (enclosed in curly braces { }) is a local variable. It is born when the block starts, and dies when the block ends. Nothing outside those curly braces can see or use it.
2. Global Variables
A variable declared outside of all functions (usually at the very top of your file) is a global variable. It is alive for the entire lifespan of your program and can be read or modified by any function.
Variable Shadowing & The Scope Resolution Operator (::)
If you have a global variable and a local variable with the exact same name, the local variable wins inside its function. This is called variable shadowing.
If you want to force C++ to look at the global variable instead of the local one, you prefix the variable name with the scope resolution operator ::.
Let’s look at this shadowing in action:
#include <iostream>
int x = 50; // Global variable x
int main() {
int x = 10; // Local variable x (shadows the global x)
std::cout << "Local x: " << x << std::endl;
std::cout << "Global x: " << ::x << std::endl; // Access global with ::
return 0;
}
Output:
Local x: 10
Global x: 50
Data Types in C++
When you store a value, the compiler needs to know its data type so it can decide:
- How to interpret the bits in memory (e.g. text vs. floating-point math).
- How many bytes of RAM to allocate.
1. Integer Types (Whole Numbers)
For counting and whole values, C++ provides int. By default, an int occupies 4 bytes of memory. You can use modifiers to change the size and range of integers:
| Type Name | Size in RAM | Value Range |
|---|---|---|
short (or short int) | 2 Bytes (16 bits) | -32,768 to 32,767 |
int | 4 Bytes (32 bits) | -2 Billion to 2 Billion |
long (or long int) | 4 Bytes (32 bits) | Same as int (minimum 32-bit) |
long long (or long long int) | 8 Bytes (64 bits) | -9 Quintillion to 9 Quintillion |
unsigned int | 4 Bytes (32 bits) | 0 to 4.29 Billion (Positive numbers only) |
Example:
#include <iostream>
int main() {
short smallNum = 10;
int regularNum = 1000;
long long hugeNum = 100000LL; // LL suffix forces compiler to read as long long
std::cout << "smallNum: " << smallNum << "\nregularNum: " << regularNum << "\nhugeNum: " << hugeNum << std::endl;
return 0;
}
Output:
smallNum: 10
regularNum: 1000
hugeNum: 100000
2. Floating-Point Types (Decimals)
For storing values with decimal points, use floating-point types. C++ has three options depending on how much precision you need:
| Type | Size in RAM | Precision |
|---|---|---|
float | 4 Bytes | ~7 decimal digits |
double | 8 Bytes | ~15 decimal digits (Default choice in C++) |
long double | 8 or 16 Bytes | Extended precision |
Example:
#include <iostream>
int main() {
float piFloat = 3.14f; // 'f' suffix specifies float literal
double piDouble = 3.1415926535;
std::cout << "float: " << piFloat << "\ndouble: " << piDouble << std::endl;
return 0;
}
Output:
float: 3.14
double: 3.14159
3. Character Type (Letters & Symbols)
For single characters, we use char. A char occupies 1 byte of memory and is always wrapped in single quotes ' '. Under the hood, C++ stores characters as ASCII integer values.
#include <iostream>
int main() {
char grade = 'A';
std::cout << "Grade: " << grade << std::endl;
return 0;
}
Output:
Grade: A
4. Boolean Type (True/False)
Booleans store logical states: true (renders as 1 in the terminal) or false (renders as 0). It uses the bool keyword and takes 1 byte of memory.
#include <iostream>
int main() {
bool isCPlusPlusCool = true;
bool isBoring = false;
std::cout << "isCPlusPlusCool: " << isCPlusPlusCool << "\nisBoring: " << isBoring << std::endl;
return 0;
}
Output:
isCPlusPlusCool: 1
isBoring: 0
Constants and Literals
Constants
If you want to prevent a variable’s value from being changed after initialization, prefix its declaration with the const keyword. This makes the variable immutable:
const double PI = 3.14159;
Trying to assign a new value to PI later in your code will trigger a compile-time compiler error.
Literals
Literals are hardcoded values written directly into your source code. Here are some examples:
#include <iostream>
int main() {
int decVal = 26; // Decimal literal
int hexVal = 0x1A; // Hexadecimal literal (starts with 0x)
int binVal = 0b11010; // Binary literal (starts with 0b)
std::cout << "decVal: " << decVal << "\nhexVal: " << hexVal << "\nbinVal: " << binVal << std::endl;
return 0;
}
Output:
decVal: 26
hexVal: 26
binVal: 26
Type Conversion in C++
Sometimes you need to convert a value from one data type to another. C++ supports two kinds of conversions:
1. Implicit Conversion (Automatic)
The compiler does this automatically when you assign a value of a smaller type to a larger type (promotions). It is safe and does not lose data.
int myInt = 10;
double myDouble = myInt; // Safe promotion: int to double
The standard implicit promotion scale ranges from smaller/restricted types to larger types:
graph LR
bool --> char --> short[short int] --> int --> unsigned_int[unsigned int] --> long --> long_long[long long] --> float --> double --> long_double[long double]
style bool fill:#e5eeff,stroke:#0058be,stroke-width:1px
style long_double fill:#d4e3ff,stroke:#0060ac,stroke-width:2px
2. Explicit Conversion (Type Casting)
When you convert a larger data type to a smaller type (e.g. converting double to int), you risk losing decimal information. The compiler will warning you, so you must explicitly instruct it to cast the values.
There are two primary ways to write explicit casts:
Static Cast (Modern C++ - Recommended)
#include <iostream>
int main() {
double decimal = 5.75;
int whole = static_cast<int>(decimal); // Truncates .75
std::cout << "whole: " << whole << std::endl;
return 0;
}
C-Style Cast (Legacy)
double decimal = 5.75;
int whole = (int)decimal; // Deprecated but widely seen
Output:
whole: 5
Working with Operators in C++
Operators are mathematical or logical symbols that process variables. We can classify C++ operators into 5 main categories:
1. Arithmetic Operators
Used for standard mathematical equations:
| Operator | Operation | Example |
|---|---|---|
+ | Addition | a + b |
- | Subtraction | a - b |
* | Multiplication | a * b |
/ | Division | a / b |
% | Modulo (Remainder) | a % b |
++ | Increment | ++a (Adds 1 to a) |
-- | Decrement | --a (Subtracts 1 from a) |
2. Logical Operators
Used to evaluate truth values between boolean expressions:
| Operator | Operation | Meaning |
|---|---|---|
&& | Logical AND | Returns true if both conditions are true (a && b) |
|| | Logical OR | Returns true if at least one condition is true (a || b) |
! | Logical NOT | Inverts the truth value (!a) |
3. Assignment Operators
Used to store values in variables, including shorthand operations:
| Operator | Example | Equivalent to |
|---|---|---|
= | a = 5 | a = 5 |
+= | a += 2 | a = a + 2 |
-= | a -= 2 | a = a - 2 |
*= | a *= 2 | a = a * 2 |
/= | a /= 2 | a = a / 2 |
%= | a %= 2 | a = a % 2 |
4. Comparison Operators
Used to compare two values, returning a boolean (true/false):
| Operator | Meaning | Example |
|---|---|---|
== | Equal to | a == b |
!= | Not equal to | a != b |
> | Greater than | a > b |
< | Less than | a < b |
>= | Greater than or equal to | a >= b |
<= | Less than or equal to | a <= b |
5. Bitwise Operators
Used for low-level byte manipulation on binary bits:
| Operator | Operation | Example (Using 5 [0101] and 3 [0011]) |
|---|---|---|
& | Bitwise AND | 0101 & 0011 -> 0001 (1 in decimal) |
| | Bitwise OR | 0101 | 0011 -> 0111 (7 in decimal) |
^ | Bitwise XOR | 0101 ^ 0011 -> 0110 (6 in decimal) |
~ | Bitwise NOT | ~0101 -> 1010 |
<< | Left Shift | 0101 << 1 -> 1010 (Shifts bits left by 1) |
>> | Right Shift | 0101 >> 1 -> 0010 (Shifts bits right by 1) |
Bitwise AND (&) Truth Table
| A | B | A & B |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 0 |
Bitwise OR (|) Truth Table
| A | B | A | B |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 0 |
Operator Precedence
When writing equations with multiple operators, C++ uses precedence rules to decide what gets calculated first. Unary operations and multiplication/division have higher priority than addition/subtraction or assignments.
[Lowest Precedence] =====================================================> [Highest Precedence]
Assignment (=) --> Logical OR (||) --> Logical AND (&&) --> Arithmetic Add (+,-) --> Arithmetic Mul (*,/) --> Unary (++,--)
Frequently Asked Questions
What is variable shadowing?
It happens when you declare a local variable inside a function with the exact same name as a global variable. The local variable temporarily hides the global variable, meaning changes inside the function only affect the local one.
What happens if I write unsigned int and assign it a negative number?
Assigning a negative value to an unsigned integer type causes integer overflow wrap-around. For example, assigning -1 to an unsigned int will actually store 4294967295 in memory (the maximum possible unsigned value).
What is the binary arithmetic behind 0101 | 0011?
Bitwise OR checks each bit column. If either bit is 1, the output is 1:
0 1 0 1 (decimal 5)
| 0 0 1 1 (decimal 3)
─────────
0 1 1 1 (decimal 7)
So the output is 0111 (which is 7 in decimal).
quiz Test Your Understanding
Which keyword makes a variable constant (unchangeable) in C++?
The const keyword prevents a variable's value from being modified after initialization.