Understand Binary, Hex And Octal

Understanding in code

In C or C++, we can represent hex and binary as:

  1. hex: 0x<HexValue>
  2. binary: 0b<Binary>
  3. octal: 0<Octet> (base 8)
TermSizeRepresentation
Bit1 bit0b1
Nibble4 bits1 Hex Digit (0xF)
Octet (or Byte)8 bits2 Hex Digits (0xFF)

For example, 0x10 this is a hex of 0000 0010 so the value would be $1 \times 16^{1}+ 0\times 16^{0} = 16$

Note: 0x10 is the same writing for 0x00000010 which is the same thing.

#include <cstypes.h>
#include <iostream>

int main() {
    int a = 0x10;
    int b = 0x00000010;
    std::cout << "a is: " << a << std::endl;
    std::cout << "b is: " << b << std::endl;
    return 0;
}
❯ make
a is: 16
b is: 16

Which ever leading 0 compiler will simple strip it.

Similarly in binary, we have the same concept: 0b10 will be $1 \times 2^{1}+ 0 \times 2^{0} = 2$

#include <cstypes.h>
#include <iostream>

int main() {
    int a = 0b10;
    int b = 0b0000000000000000000000000000000000000000010;
    std::cout << "a is: " << a << std::endl;
    std::cout << "b is: " << b << std::endl;
    return 0;
}
❯ make
a is: 2
b is: 2

For octet, the leading is 0, so if 010 will be $1 \times 8^{1}+ 0 \times 8^{0} = 8$

#include <cstypes.h>
#include <iostream>

int main() {
    int a = 010;
    int b = 00000000000000000000000000000000000000000010;
    std::cout << "a is: " << a << std::endl;
    std::cout << "b is: " << b << std::endl;
    return 0;
}
❯ make
a is: 8
b is: 8

Normally integer is a base 10 so it makes sense.

Value for each type

  1. Binary: [0, 1]
  2. Hex:
    • [0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F]
    • A -> F represent 10 → 15
  3. Octal:
    • [0,1,2,3,4,5,6,7]

Using with cstdint

cstdint allow us to have a fine gain of the memory, where the smallest unit is 1 byte = 8 bit. For example

#include <cstdint>
#include <iostream>

int main() {
    uint8_t a = 0x1;
    std::cout << sizeof(a) << std::endl;
    return 0;
}
❯ make
1

This will be exactly 1 bit, since we're using uint8_t which means we have 8 bits (1 byte) representation: 0000 0001. This is the same thing as

#include <cstdint>
#include <iostream>

int main() {
    uint8_t a = 0b00000001;
    std::cout << sizeof(a) << std::endl;
    return 0;
}

But the moment we exceed 1 byte, it' gonna complain

#include <cstdint>
#include <iostream>

int main() {
    uint8_t a = 0b100000001; // 9 bits
    std::cout << sizeof(a) << std::endl;
    return 0;
}
❯ make
main.cpp: In function ‘int main()’:
main.cpp:5:17: warning: unsigned conversion from ‘int’ to ‘uint8_t’ {aka ‘unsigned char’} changes value from ‘257’ to ‘1’ [-Woverflow]
    5 |     uint8_t a = 0b100000001;
      |                 ^~~~~~~~~~~
1

Similarly, for hex:

#include <cstdint>
#include <iostream>

int main() {
    uint8_t a = 0x123; // Exceed 8 bit
    std::cout << sizeof(a) << std::endl;
    return 0;
}
❯ make
main.cpp: In function ‘int main()’:
main.cpp:5:17: warning: unsigned conversion from ‘int’ to ‘uint8_t’ {aka ‘unsigned char’} changes value from ‘291’ to ‘35’ [-Woverflow]
    5 |     uint8_t a = 0x123;
      |                 ^~~~~
1

If we use uint16_t, it will show as 2 bytes (16 bits)

#include <cstdint>
#include <iostream>

int main() {
    uint16_t a = 0x123;
    std::cout << sizeof(a) << std::endl;
    return 0;
}

Understanding Hex Editor

Pasted image 20260125001507.png

In the middle row, a hex value is a combination of 2 number 0x??. Why 2 number? because it sum up to 1 byte (8 bit) since 1 hex number = 4 byte (1 nibble). The right handside is the display, it's pretty much the same as printing (char*) value.

Pasted image 20260125004238.png

#include <cstdint>
#include <iostream>

int main() {
    uint8_t a = 0x61;
    std::cout << a << std::endl;
    return 0;
}
❯ make
a