At its core, a computer is simply storing and manipulating data. Before we can understand memory, functions, binaries or vulnerabilities, we first need to understand the most fundamental building blocks that software operates on: data types.
A data type defines what kind of information a variable can store and how much memory is required to store it. Different programming languages may implement them slightly differently, but the underlying concepts remain largely the same.
The exact size of some types can vary between operating systems, architectures and compilers. For the purpose of this course, focus on the concepts rather than memorizing every possible size.
| Data Type | Example | Typical Purpose |
|---|---|---|
char | 'A' | Single character |
bool | true | True/False value |
short | 123 | Small integer |
int | 1337 | Standard integer |
long | 1000000 | Larger integer |
float | 3.14 | Decimal number |
double | 3.1415926535 | Higher precision decimal |
A useful way to think about data types is that they tell the compiler how to interpret a particular region of memory. The binary value 65 could represent the integer 65, the character 'A', part of a floating-point number or something else entirely depending on how the program chooses to interpret it.
In the next section we will build upon these primitive types to create more complex structures such as arrays and strings.
Size of Data Types
From a low-level perspective, data types are ultimately just regions of memory with a defined size.
Modern processors operate on data in fixed widths, so programming languages typically provide data types that map cleanly to these sizes. Although exact sizes can vary between architectures and compilers, you will frequently encounter values such as:
| Size | Bits |
|---|---|
| 1 Byte | 8 Bits |
| 2 Bytes | 16 Bits |
| 4 Bytes | 32 Bits |
| 8 Bytes | 64 Bits |
For example, an int is commonly 4 bytes on modern systems, while a pointer on a 64-bit operating system is typically 8 bytes.
Understanding these sizes becomes increasingly important in security research because memory corruption vulnerabilities, binary analysis, reverse engineering and exploit development often depend on knowing exactly how many bytes are being read, written or interpreted by a program.
At the machine level, the computer does not inherently understand concepts such as "integer" or "character"—it simply processes sequences of bytes. Data types exist to help both the programmer and the compiler agree on how those bytes should be interpreted.
Numeric Data Types
Most software spends a significant amount of time working with numbers. Whether we are counting users, calculating network packet lengths, storing process identifiers or tracking memory addresses, numeric data types are everywhere.
The most common numeric types are:
| Type | Example | Purpose |
|---|---|---|
short | 123 | Small integer |
int | 1337 | Standard integer |
long | 1000000 | Larger integer |
float | 3.14 | Decimal number |
double | 3.1415926535 | Higher precision decimal |
Integers (short, int, long) store whole numbers, while floating-point types (float, double) store decimal values.
Signed vs Unsigned
Numeric types can be either signed or unsigned.
A signed type can represent both positive and negative numbers:
int number = -42;
An unsigned type can only represent positive values:
unsigned int number = 42;
Since unsigned types do not need to store a sign bit, they can represent a larger positive range using the same amount of memory.
For example, a 32-bit signed integer typically ranges from:
-2,147,483,648 to 2,147,483,647
While a 32-bit unsigned integer typically ranges from:
0 to 4,294,967,295
This distinction becomes important in security because many vulnerabilities originate from developers making incorrect assumptions about numeric ranges, signed-ness or arithmetic operations.
Characters and ASCII
Computers fundamentally store numbers, not letters. To represent text, characters are mapped to numeric values using standardized encoding schemes.
Historically, the most important of these was ASCII (American Standard Code for Information Interchange).
ASCII assigns a numeric value to each character:
| Character | Decimal Value |
|---|---|
A | 65 |
B | 66 |
a | 97 |
0 | 48 |
| Space | 32 |
In C, a character is stored using the char data type:
char letter = 'A';
Internally, the variable above contains the numeric value 65.
This means the following are effectively equivalent:
char letter = 'A';
char letter = 65;
You can observe this directly:
printf("%d", 'A');
Output:
65
Understanding that characters are ultimately just numbers is an important step toward understanding strings, memory layouts, binary formats and many classes of software vulnerabilities.
Modern systems frequently use Unicode-based encodings such as UTF-8 to support a much larger range of characters than traditional ASCII, but the underlying concept remains the same: text is ultimately represented as numbers stored in memory.