Coding 101

Memory, Types, and Representation: What Actually Happens When You Declare a Variable

DD
Ankur Ishwar
10 min read Updated Sep 7, 2026
Memory allocation and data types diagram

When programming courses introduce variables, they usually tell you: "A variable is like a cardboard box with a name on it where you store things." That metaphor works for five minutes until you hit your first memory leak, integer overflow, or reference mutation bug in production.

In actual computer hardware, variables are human-readable symbols mapped to physical memory addresses in RAM. What type of data you place in that slot determines how many bits the CPU reads and how it interprets those bits.

The Physical Reality: Stack vs Heap Memory

Whenever your program executes a function, the operating system assigns it two distinct zones of memory: the Stack and the Heap.

Characteristic Stack Memory Heap Memory
Allocation Speed Instant (increments a CPU stack pointer register) Slower (requires allocator search for free block)
Structure Strict LIFO (Last-In, First-Out) call frames Dynamic, unorganized pool of memory blocks
Data Stored Primitives, local integers, memory addresses (pointers) Objects, dynamic arrays, hash tables, closures
Deallocation Automatic as soon as function returns Garbage collector scan or manual free()

When you declare const balance = 5000; inside a JavaScript or C function, the runtime writes that integer directly into the active stack frame. When the function returns, the CPU moves the stack pointer back, reclaiming that memory in a single clock cycle with zero garbage collection overhead.

When you declare an object like const user = { name: "Rahul", role: "Admin" };, the object body lives on the heap. The variable user on the stack simply holds a 64-bit integer address pointing to that heap location.

Integers and Two's Complement: The Overflow Trap

How does a computer represent negative numbers using only binary ones and zeros? It uses Two's Complement representation.

In a standard 32-bit signed integer, the leftmost bit is the sign bit (0 for positive, 1 for negative). The remaining 31 bits encode the value. This yields an exact range:

  • Minimum value: -2,147,483,648 (-2^31)
  • Maximum value: 2,147,483,647 (2^31 - 1)

What happens when you add 1 to the maximum 32-bit integer in C, Java, or Go?

int max_val = 2147483647;
max_val = max_val + 1;
// Result wraps around to: -2147483648

The bits roll over into the sign bit. This exact overflow bug crashed the Ariane 5 rocket in 1996 and broke YouTube's view counter on the Gangnam Style music video when it surpassed 2.14 billion views. In JavaScript, all numbers historically used 64-bit floats with safe integer precision up to Number.MAX_SAFE_INTEGER (9,007,199,254,740,991). To handle larger integers in modern JS without silent bit corruption, use native BigInt.

The Floating-Point Lie: Why 0.1 + 0.2 Is Not 0.3

Open your browser console right now and type 0.1 + 0.2 === 0.3. It evaluates to false. In fact, 0.1 + 0.2 prints 0.30000000000000004.

This is not a bug in JavaScript, Python, or C++. It is a mathematical consequence of the IEEE 754 standard for floating-point arithmetic.

Computers work in base-2 (binary). In base-10, the fraction 1/3 cannot be represented with finite decimals (0.333333...). Similarly, in base-2, numbers like 0.1 (1/10) and 0.2 (1/5) become repeating infinite binary fractions:

0.1 in binary = 0.00011001100110011001100110011... (repeats forever)

Because hardware registers have finite width (53 bits for the mantissa in a double-precision float), the number is rounded. Those tiny rounding errors accumulate across thousands of calculations.

The Golden Rule of Financial Engineering

Never store monetary amounts in floating-point data types. If an e-commerce platform tracks ₹99.90 as a float across 100,000 orders, you will lose real rupees to rounding errors during tax and balance reconciliation.

Store money as integers representing the smallest currency unit (paise in India, cents in the US):

// Danger: floating-point drift
const itemPrice = 19.99;
const tax = itemPrice * 0.18;

// Production standard: integer arithmetic in paise
const priceInPaise = 1999n; // ₹19.99 stored as BigInt integer
const taxRateBasisPoints = 1800n; // 18.00%
const taxInPaise = (priceInPaise * taxRateBasisPoints) / 10000n; // Exact integer math

Pass-by-Value vs Pass-by-Reference: Mutation Bugs

One of the most common bugs in full-stack applications is unintentional state mutation caused by misunderstanding pointer references.

// Primitive: Pass-by-Value (Clean copy)
let balanceA = 500;
let balanceB = balanceA;
balanceB += 200;
console.log(balanceA); // 500 (unaffected)

// Object: Pass-by-Reference (Shared pointer)
const accountA = { holder: "Vikram", balance: 500 };
const accountB = accountA; // accountB points to the exact same heap address
accountB.balance += 200;
console.log(accountA.balance); // 700! accountA was silently mutated

In JavaScript and Python, primitives (numbers, booleans, strings) are copied by value. Objects, arrays, and dictionaries copy their reference address. When you pass an object into a helper function, that function can mutate the caller's state unless you clone it defensively.

Shallow Copy vs Deep Copy

The spread operator ({ ...accountA }) only creates a shallow copy. If your object contains nested arrays or objects, the inner references remain shared:

const user = {
  id: 101,
  preferences: { theme: 'dark', notifications: true }
};

// Shallow copy leaves nested objects linked
const shallowClone = { ...user };
shallowClone.preferences.theme = 'light';
console.log(user.preferences.theme); // 'light' (mutated!)

// True deep copy in modern JavaScript runtimes
const deepClone = structuredClone(user);
deepClone.preferences.theme = 'midnight';
console.log(user.preferences.theme); // 'light' (completely isolated)

String Immutability: Memory Allocation in V8 and Python

Many beginners think strings behave like mutable character arrays. In both JavaScript and Python, strings are strictly immutable.

When you concatenate strings in a loop:

# Bad: Allocates a new heap buffer on every iteration
result = ""
for i in range(10000):
    result += str(i) + ","

Every single += creates a brand new string in memory, copies the old characters over, and leaves the old string for garbage collection. Doing this on large datasets tanks performance from O(N) to O(N^2).

The correct pattern is appending chunks into an array and joining them once:

# Fast O(N): Accumulate in list, allocate string once
chunks = []
for i in range(10000):
    chunks.append(str(i))
result = ",".join(chunks)

Frequently Asked Questions

Why does Python not require explicit type declarations?

Python is dynamically typed: types are bound to values in memory at runtime, not to variable names. Each Python object contains a type header and a reference count in its C structure (PyObject). Languages like TypeScript and Go enforce types at compile time, catching type mismatches before the program runs.

How can I safely compare two floating-point numbers?

Never compare floats with strict equality (a === b). Instead, verify that the absolute difference between them is smaller than a tiny threshold called epsilon: Math.abs(a - b) < Number.EPSILON.

What is the difference between null and undefined in JavaScript?

undefined means a variable has been declared on the stack but has not yet been assigned any value. null is an intentional assignment representing the absence of an object value.

Found this useful?
View all articles
Free Technical Interview Prep

Practicing for Engineering Interviews?

Skip the expensive coaching bootcamps and dry LeetCode memorization. Practice real production scenarios with instant turn-by-turn AI feedback on Frontend, Backend, System Design, and DSA.

Free Utilities

Recommended Developer Tools for this Topic

Explore all 25+ tools→

Keep Reading

Related Articles

Learn with Dropout Developer

Build real software with AI

Step-by-step learning paths, vibe coding tutorials, and certified developer programs designed for the modern engineer.