Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Data Storage in Memory (C Language)

Tech 2

Data Storage in Memory (C Language)

Basic Built-in Types:

  • char: 1 byte
  • short: 2 bytes
  • int: 4 bytes
  • long: 4/8 bytes (system - dependent)
  • long long: 8 bytes
  • float: 4 bytes
  • double: 8 bytes

Type Fmailies:

Integer Family:
  • char

  • unsigned char, signed char
    (Note: char stores ASCII values, so it belongs to the integer family.)

  • short

  • unsigned short, signed short

  • int

  • unsigned int, signed int

  • long

  • unsigned long, signed long

Floating - Point Family:
  • float
  • double
Constructed (User - Defined) Types:
  • Arrays: int arr[10] → Type: int[10]
  • Structs: struct
  • Enums: enum
  • Unions: union
Pointer Types, Void Type (Briefly Mentioned)

Data Storage in Memory

Integers:

Integers have three binary representations: sign - magnitude (original code), one's complement, and two's complement.
For positive integers, all three codes are identical. We focus on negative numbers:

Example:

int num = -10;
// Sign - magnitude: 10000000000000000000000000001010
// One's complement: 11111111111111111111111111110101 (flip bits except sign)
// Two's complement: 11111111111111111111111111110110 (one's complement + 1)

Computers store integers using two's complement! (Critical!)
An int uses 32 bits. In hex, -10 is 0xfffffff6. On a little - endian machine, bytes are stored in reverse: f6 ff ff ff.

Endianness:

Assume lower addresses are on the left, higher on the right. Storing 0x11223344:

  • Big - endian: Bytes are 11 22 33 44 (high byte at low address).
  • Little - endian: Bytes are 44 33 22 11 (low byte at low address).

Definition:

  • Little - endian: Least significant byte (LSB) at the lowest address.
  • Big - endian: Most significant byte (MSB) at the lowest address.

Detecting Endianness (Code Example):

int checkEndian() {
    int val = 1; // Binary: 00000000 00000000 00000000 00000001
    return *(char*)&val; // Check the first byte
}

int main() {
    int result = checkEndian();
    if (result == 1) {
        printf("Little - endian\n");
    } else {
        printf("Big - endian\n");
    }
    return 0;
}

Explanation: For val = 1 (hex 0x00000001):

  • Little - endian: First byte is 01 (LSB), so *(char*)&val is 1.
  • Big - endian: First byte is 00 (MSB), so *(char*)&val is 0.

Integer Range & Promotion:

Take signed char and unsigned char as examples:
Binary values: 00000000 (0) → 01111111 (127) → 10000000 (-128) → 11111111 (-1).

Integer Promotion:

  • For signed types: Extend with the sign bit (0 for positive, 1 for negative).
    E.g., signed char (8 bits) → int (32 bits): 0b11111111 (char) becomes 0b11111111111111111111111111111111 (int).
  • For unsigned types: Extend with 0s.

Sign - Magnitude ↔ Two's Complement:

  • Sign - Magnitude → Two's Complement:

    1. Keep the sign bit, flip all other bits (one's complement).
    2. Add 1 to the one's complement (two's complement).
  • Two's Complement → Sign - Magnitude:
    Either:

    • Subtract 1, then flip all bits (excluding sign).
    • Or: Flip all bits, then add 1.

Floating - Point Numbers:

A floating - point number V is represented as:
V = (-1)^S × M × 2^E

Example: 5.5_{10} = 101.1_2 = (-1)^0 × 1.011 × 2^2

IEEE 754 Standard:
  • 32 - bit (float): 1 sign bit (S), 8 exponent bits (E), 23 mantissa bits (M).
  • 64 - bit (double): 1 sign bit (S), 11 exponent bits (E), 52 mantissa bits (M).
Mantissa (M) Handling:

Since 1 ≤ M < 2, M is stored as 1.xxxxxx (binary). The leading 1 is omitted to save space (e.g., 1.01 → store 01).

Exponent (E) Handling:
  • E is stored with a bias: Add 127 (for 8 - bit E) or 1023 (for 11 - bit E) to the actual exponent.
  • Example: For 2^10, E is stored as 10 + 127 = 137 (binary 10001001).
Special Cases for E:
  • E = 0 (all bits 0):

    • Exponent is 1 - bias (e.g., 1 - 127 = -126 for 8 - bit E).
    • Mantissa M is 0.xxxxxx (no leading 1).
    • Used for ±0 or very small numbers.
  • E = 1 (all bits 1):

    • If M = 0: Represents ±infinity (sign determined by S).

Code Verification (Floating - Point vs Integer):

#include <stdio.h>

int main() {
    int val = 9;
    float* floatPtr = (float*)&val;

    printf("Value of val: %d\n", val);          // Output: 9
    printf("Value of *floatPtr: %f\n", *floatPtr); // Output: 0.000000

    *floatPtr = 9.0;
    printf("Value of val: %d\n", val);          // Output: 1091567616
    printf("Value of *floatPtr: %f\n", *floatPtr); // Output: 9.000000

    return 0;
}

Explanation:

  1. Storing 9 (int: 00000000 00000000 00000000 00001001) as a float:

    • S = 0, E = 0 (all bits 0), M = 000000000000000000001001.
    • Exponent: 1 - 127 = -126, Mantissa: 0.00000000000000000001001.
    • Result: (-1)^0 × 0.00000000000000000001001 × 2^-126 (extremely small, prints as 0.000000).
  2. Storing 9.0 (float):

    • 9.0 = 1001.0_2 = 1.001 × 2^3.
    • S = 0, E = 3 + 127 = 130 (binary 10000010), M = 00100000000000000000000.
    • Binary representation: 01000001000100000000000000000000 (hex 0x41200000 → decimal 1091567616).

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.