Variables: named storage

A program needs to remember things while it runs: a price, a name, how many items are in a basket. A variable is a name attached to a stored value, so the program can refer to the value by name, read it, and replace it with a new one. "Variable" means exactly that: the value may vary while the program runs.

price ← 40
quantity ← 3
total ← price * quantity
quantity ← quantity + 1

The arrow reads "becomes": after the last line, quantity holds 4, while total still holds 120 — it was computed from the old value and is not recalculated automatically. Languages differ in what the name is attached to. In C, a variable is a fixed piece of memory of a known size. In Python, a variable is a name that refers to an object stored elsewhere; the Python page Variables and Data Types explains that model, and Memory Management shows where the values live.

Identifiers and naming rules

The name of a variable, constant, function or type is called an identifier. Each language has rules for which names are allowed, and the rules are remarkably similar:

Rule Python C JavaScript
Allowed characters letters, digits, _ letters, digits, _ letters, digits, _, $
May start with a digit no no no
Case-sensitive (total ≠ Total) yes yes yes
Keywords allowed as names no (a few soft keywords excepted) no no (a few contextual keywords excepted)
Usual style for variables unit_price unit_price unitPrice
Usual style for constants VAT_RATE VAT_RATE VAT_RATE

Spaces and hyphens are never allowed inside a name, which is why multi-word names are joined with underscores (snake_case) or capitals (camelCase). Not every language is case-sensitive: SQL keywords and, in most databases, unquoted table and column names are not. Beyond the rules, choose names that say what the value means: seconds_elapsed tells the reader more than t or x2.

Constants

A constant is a named value that does not change after it has been set. Tax rates, the number of days in a week, a maximum file size and π are typical constants. Giving such a value a name has three benefits: the code explains itself (price * VAT_RATE instead of price * 0.21), the value is defined in one place when it does need updating, and the language can stop you from changing it by accident. How strict that protection is differs per language.

C: const

In C, the keyword const makes a variable read-only. The compiler refuses any later assignment to it.

#include <stdio.h>

int main(void) {
    const double VAT_RATE = 0.21;
    double net = 100.0;

    printf("%.2f\n", net * (1 + VAT_RATE));
    return 0;
}
121.00

Adding the line VAT_RATE = 0.19; makes the program fail to compile, with error: assignment of read-only variable 'VAT_RATE'. The mistake is caught before the program ever runs.

JavaScript: const

JavaScript's const declares a name that cannot be assigned again. The check happens while the program runs. Note what is constant: the binding between the name and the value, not the contents of the value. An array declared with const can still be changed.

const VAT_RATE = 0.21;
const prices = [10, 20];

prices.push(30);          // allowed: the array itself is changed
console.log(prices);

VAT_RATE = 0.19;          // not allowed: VAT_RATE is reassigned
[ 10, 20, 30 ]
TypeError: Assignment to constant variable.

Python: a naming convention

Python has no constant keyword. By convention, a name written in UPPER_CASE at the top of a module is a constant, and programmers do not assign to it. The interpreter does not enforce this; a type checker such as mypy can, if you annotate the name with typing.Final.

VAT_RATE = 0.21          # a constant by convention only
DAYS_PER_WEEK = 7

net = 100.0
print(round(net * (1 + VAT_RATE), 2))
print(DAYS_PER_WEEK * 52)
121.0
364

Common data types

A value's data type says what kind of value it is, which tells the computer how to store it and which operations make sense. You can multiply two numbers, but not two names; you can make a name uppercase, but not a number. The names differ per language, but the same basic, or primitive, types appear nearly everywhere:

Type Holds Examples C Python JavaScript
Integer whole numbers 42, -7 int, long int number (also bigint)
Floating point numbers with a fraction 3.14, -0.5 float, double float number
Character one single character 'A', '7' char none (a string of length 1) none (a string of length 1)
String a sequence of characters: text "Hello", "42" array of char str string
Boolean true or false true, false bool (<stdbool.h>) bool boolean

In most compiled languages, number types have a fixed size. A C int is usually 32 bits, so it can only hold values up to 2,147,483,647; a larger result overflows. Python's int grows as needed and never overflows. Floating-point numbers trade exactness for range: 0.1 + 0.2 is not exactly 0.3 in any of these languages. Number Systems and Data Representation explains both effects.

Characters and strings

A character (char) is a single letter, digit, space or symbol. Underneath, it is stored as a number: its code in a character set such as ASCII. C makes this visible, because a char is simply a small integer:

#include <stdio.h>

int main(void) {
    char grade = 'B';

    printf("%c %d\n", grade, grade);   /* as a character, then as a number */
    printf("%c\n", grade + 1);         /* the next character code */
    return 0;
}
B 66
C

A string is a sequence of characters. Many languages write a single character in single quotes and a string in double quotes. In C, a character constant such as 'A' actually has type int (so sizeof('A') is usually 4), and is normally stored in a char; only in C++ is it a char itself. "A" is a string of one character followed by a hidden end marker. Python and JavaScript have no separate character type, and accept either kind of quote for strings. How characters become numbers is the subject of Character Encoding.

Composite types

Composite (or compound) types are built from other types. An array or list holds several values in order, such as a week of temperatures. A record — a struct in C, an object in JavaScript, a class instance or dictionary in Python — groups named fields that belong together, such as a customer's name, email address and date of birth. Strings are often counted here too, since they are sequences of characters. Data Structures covers the common composite types and what each is good at.

Static and dynamic typing

Every value has a type, but languages differ in when they check that types are used correctly. With static typing, each variable has a type that is fixed in the source code, and the compiler checks every use of it before the program runs. C, C++, Java, C#, Go and Rust are statically typed. With dynamic typing, types belong to the values, not to the variables; a variable can refer to a number now and to a string later, and types are checked while the program runs. Python, JavaScript, Ruby and PHP are dynamically typed.

Static typing in C

In C, the type is part of the declaration, and it stays fixed for the variable's whole life. Assigning a value of another numeric type does not change the variable's type; the value is converted to fit.

#include <stdio.h>

int main(void) {
    int quantity = 3;        /* quantity is an int, for good */
    double price = 2.50;

    quantity = 7;            /* fine: another int */
    quantity = 7.9;          /* converted to int: the fraction is dropped */
    printf("%d items at %.2f\n", quantity, price);
    return 0;
}
7 items at 2.50

Try to store text in quantity and the compiler complains before anything runs. GCC 14 and later stop with an error; older versions give the same message as a warning:

quantity = "three";
error: assignment to 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]

Dynamic typing in Python

In Python, the same name can refer to values of different types over time. Each value knows its own type, and Python checks whether an operation fits only when that line runs.

quantity = 3
print(type(quantity).__name__)

quantity = "three"
print(type(quantity).__name__)
print(quantity.upper())
int
str
THREE

The flexibility has a price: a type mistake is only found when the faulty line runs — or not at all, if the operation happens to be valid for the wrong type:

def order_total(price, quantity):
    return price * quantity


print(order_total(2.5, 4))
print(order_total("2.5", 4))    # a string by mistake: repeated, not multiplied
10.0
2.52.52.52.5

Static typing catches this class of mistake early and helps editors with autocompletion; dynamic typing makes short programs quicker to write and experiment with. The two approaches have moved toward each other: Python has optional type hints checked by tools such as mypy, and TypeScript adds static types on top of JavaScript.

Type inference

Static typing does not always mean writing every type out. Type inference lets the compiler work out a variable's type from the value it is given. In Go, count := 3 declares an int; Rust's let count = 3;, Java's var count = 3;, C#'s var and C++'s auto do the same. The type is still fixed at compile time — assigning a string to count later is still an error — you just do not have to spell it out.

Strong and weak typing

A second, separate question is what happens when an operation receives values of types that do not match. With strong typing, the language refuses to combine unrelated types and reports an error; you must convert explicitly. With weak typing, the language silently converts one value to another type so the operation can go ahead. Unlike static and dynamic, "strong" and "weak" are not formally defined: they describe a scale, and languages sit at different points on it.

JavaScript is the classic weakly typed example. Its + operator joins strings when either side is a string, but * and - only work on numbers, so they convert strings to numbers instead:

console.log("5" + 1);     // number converted to string, then joined
console.log("5" * 2);     // string converted to number, then multiplied
console.log("5" - 1);
console.log(true + 1);    // true becomes 1
51
10
4
2

Python, which is also dynamically typed, is strongly typed. It does not guess what you mean:

print(5 * 2)
print("5" + 1)
10
TypeError: can only concatenate str (not "int") to str

The two scales combine freely. Python is dynamic and strong; JavaScript is dynamic and weak; Java is static and fairly strong. C is static but is often called weak, because it converts between numeric types silently and lets pointer casts reinterpret memory as any type.

Casting and coercion

Converting a value from one type to another happens in two ways. An explicit conversion, also called a cast, is written by the programmer: int("42") in Python, (double) total in C, Number("42") in JavaScript. An implicit conversion, or coercion, is done by the language on its own, following fixed rules. The JavaScript examples above are coercions.

Even strongly typed languages coerce in safe cases, typically widening an integer to a floating-point number when the two are mixed. The C example below shows why the moment of conversion matters: integer division happens first unless you cast one operand before dividing.

#include <stdio.h>

int main(void) {
    int total = 7;
    int count = 2;

    printf("%d\n", total / count);             /* int / int: whole-number division */
    printf("%.1f\n", (double) total / count);  /* cast first, then divide */

    double average = total / count;            /* divided as ints, THEN converted */
    printf("%.1f\n", average);
    return 0;
}
3
3.5
3.0
age_text = "42"
print(int(age_text) + 1)      # explicit: string to int
print(str(42) + " years")     # explicit: int to string
print(3 + 0.5)                # implicit: int widened to float
print(int(3.99))              # explicit: the fraction is cut off
43
42 years
3.5
3

A conversion can lose information (3.99 becomes 3) or fail (int("forty-two") raises a ValueError), which is one reason strongly typed languages make you ask for it. The Python details are in Variables and Data Types.

No value: null, None and undefined

Sometimes a variable has no meaningful value yet, or a search finds nothing. Most languages have a special marker for "no value", and it is deliberately different from 0, from an empty string and from false, because those are all real values.

Language Marker Meaning
C NULL a pointer that points to nothing
Java, C# null a reference that refers to no object
JavaScript undefined not assigned at all (the automatic default)
JavaScript null deliberately empty, set by the programmer
Python None the single "no value" object
SQL NULL unknown or missing value in a column
let middleName;                     // declared, never assigned
console.log(middleName);
console.log(typeof null);           // a famous historical quirk
console.log(null == undefined);     // loose equality: treated as equal
console.log(null === undefined);    // strict equality: different values
undefined
object
true
false
prices = {"apple": 0.50, "pear": 0.75}

price = prices.get("mango")    # no such key: get() returns None
print(price)
print(price is None)
None
True