Hệ Thập Lục Phân Trong C: Hướng Dẫn Từ Cơ Bản Đến Nâng Cao

1. Giới thiệu

Ngôn ngữ lập trình C là một ngôn ngữ mạnh mẽ được nhiều lập trình viên sử dụng, cung cấp nhiều cách xử lý số liệu hiệu quả. Trong đó, “hệ thập lục phân” (hexadecimal) được dùng thường xuyên trong các thao tác bit và quản lý bộ nhớ. Bài viết này sẽ giải thích chi tiết từ cơ bản đến nâng cao về hệ thập lục phân trong C. Nội dung được trình bày tuần tự để người mới tiếp cận cũng có thể hiểu rõ.

2. Cách biểu diễn số

Sự khác nhau giữa hệ thập phân, bát phân và thập lục phân

Trong C, số có thể được biểu diễn như sau:

  1. Hệ thập phân
    Cách biểu diễn số quen thuộc mà con người sử dụng hàng ngày. Ví dụ, 123 được coi là số ở hệ thập phân.
  2. Hệ bát phân
    Thêm 0 ở đầu số để biểu diễn ở hệ bát phân. Ví dụ, 0123 ở hệ bát phân tương đương với “83” ở hệ thập phân.
  3. Hệ thập lục phân
    Thêm 0x hoặc 0X ở đầu số để biểu diễn ở hệ thập lục phân. Ví dụ, 0x123 ở hệ thập lục phân tương đương với “291” ở hệ thập phân.

Ưu điểm của hệ thập lục phân

Hệ thập lục phân được sử dụng như dạng rút gọn của hệ nhị phân. Mỗi chữ số thập lục phân biểu diễn được 4 bit, rất tiện lợi cho các thao tác ở mức bit. Ngoài ra, nó được dùng nhiều khi kiểm tra nội dung bộ nhớ trong quá trình gỡ lỗi (debug).

侍エンジニア塾

3. Cơ bản về hệ thập lục phân

Cấu trúc của hệ thập lục phân

Hệ thập lục phân gồm 16 ký hiệu sau:

  • 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
    Trong đó, A là 10, B là 11, …, F là 15.

Chuyển đổi từ thập lục phân sang thập phân

Ví dụ, để đổi 0x2F từ hệ thập lục phân sang thập phân:

  • 0x2F = (2 × 16) + (15 × 1) = 47

Chuyển đổi từ thập phân sang thập lục phân

Để đổi 47 từ hệ thập phân sang thập lục phân:

  1. Chia cho 16 (47 ÷ 16 = 2 dư 15).
  2. Lấy thương làm chữ số cao và dư làm chữ số thấp.
  • Kết quả: 0x2F

4. Cách viết hằng số thập lục phân trong C

Cách viết literal

Trong C, số thập lục phân được viết như sau:

int hexValue = 0x1A; // 26 ở hệ thập phân

Xuất số thập lục phân bằng printf

Để hiển thị số thập lục phân trong C, dùng hàm printf:

#include <stdio.h>

int main() {
    int hexValue = 0x1A;
    printf("Hex: %X
", hexValue); // hiển thị chữ in hoa
    printf("Hex: %x
", hexValue); // hiển thị chữ thường
    return 0;
}

Kết quả:

Hex: 1A
Hex: 1a

Gán vào biến kiểu số nguyên

Số thập lục phân có thể gán cho biến kiểu số nguyên.

int num = 0xFF; // được coi là 255

5. Hệ thập lục phân và phép toán bit

Sử dụng bitmask

Hệ thập lục phân thường được sử dụng trong các thao tác bitmask. Ví dụ:

#include <stdio.h>

int main() {
    unsigned char value = 0xAF; // "AF" ở hệ thập lục phân
    unsigned char mask = 0x0F;  // mặt nạ để lấy 4 bit thấp

    unsigned char result = value & mask;
    printf("Kết quả: 0x%X
", result); // Kết quả là "0x0F"
    return 0;
}

Thiết lập bit flag

Để đặt một bit cụ thể thành 1:

unsigned char flags = 0x00;
flags |= 0x10; // Đặt bit thứ 4 thành 1

6. Hệ thập lục phân và số thực dấu phẩy động

Biểu diễn số thực dấu phẩy động ở dạng thập lục phân là một trường hợp đặc biệt, nhưng có thể thực hiện với printf:

#include <stdio.h>

int main() {
    double value = 123.456;
    printf("Dạng hex: %a
", value);
    return 0;
}

Ví dụ kết quả:

Dạng hex: 0x1.ed70a3d70a3d7p+6

7. Ứng dụng của hệ thập lục phân

Phân tích bộ nhớ dump

Ví dụ kiểm tra nội dung bộ nhớ khi debug bằng hệ thập lục phân:

#include <stdio.h>

void dumpMemory(void* ptr, size_t size) {
    unsigned char* byte = (unsigned char*)ptr;
    for (size_t i = 0; i < size; i++) {
        printf("%02X ", byte[i]);
        if ((i + 1) % 16 == 0) printf("
");
    }
}

int main() {
    int data = 0x12345678;
    dumpMemory(&data, sizeof(data));
    return 0;
}

8. Tổng kết

Bài viết đã trình bày từ kiến thức cơ bản đến các ứng dụng thực tế của hệ thập lục phân trong C. Hệ thập lục phân không chỉ hữu ích cho biểu diễn số mà còn rất quan trọng trong thao tác bit và debug. Khi lập trình C, hãy dành thời gian nắm vững hệ thập lục phân để tận dụng tối đa sức mạnh của nó.

FAQ: Câu hỏi thường gặp về hệ thập lục phân trong C

Q1: Nếu quên thêm 0x khi khai báo số thập lục phân thì sao?

Nếu quên thêm 0x (hoặc 0X) ở đầu, số sẽ được hiểu là hệ thập phân.
Ví dụ:

int value = 123; // đây là 123 ở hệ thập phân

Muốn dùng hệ thập lục phân thì bắt buộc phải thêm 0x.

Q2: Làm sao nhập số thập lục phân bằng scanf?

Dùng định dạng %x với scanf.
Ví dụ:

#include <stdio.h>

int main() {
    int value;
    printf("Nhập số hex (ví dụ: 0x1A): ");
    scanf("%x", &value); // đọc số hex
    printf("Giá trị thập phân: %d
", value);
    return 0;
}

Q3: Cách xử lý số thập lục phân như chuỗi?

Có thể dùng hàm xử lý chuỗi hoặc định dạng xuất để chuyển số sang chuỗi hex.
Ví dụ:

#include <stdio.h>

int main() {
    int value = 255;
    char hexStr[10];
    sprintf(hexStr, "%X", value); // chuyển số sang chuỗi hex
    printf("Chuỗi hex: %s
", hexStr);
    return 0;
}

Q4: Nếu số hex không đủ chữ số thì sao?

Trong C, số hex có thể bỏ bớt các số 0 ở đầu mà vẫn hoạt động bình thường. Ví dụ 0xA giống 0x000A. Tuy nhiên, để dễ đọc mã, nên thêm số 0 nếu cần.

Q5: Nguyên nhân thường gặp khi bị lỗi khi xử lý số hex?

  1. Quên thêm 0x
  • Giải pháp: Luôn thêm 0x hoặc 0X khi viết số hex.
  1. Sai định dạng xuất/nhập
  • Ví dụ: dùng %d để xuất số hex bằng printf.
  • Giải pháp: Dùng %x hoặc %X cho số hex.
  1. Sai kiểu dữ liệu
  • Ví dụ: không dùng unsigned cho số hex và bị gắn dấu âm.
  • Giải pháp: Thêm unsigned nếu cần.

Q6: Cách đơn giản để đổi thập phân sang hex?

Dùng printf.
Ví dụ:

#include <stdio.h>

int main() {
    int decimalValue = 42;
    printf("Thập phân: %d -> Hex: %X
", decimalValue, decimalValue);
    return 0;
}

Q7: Có thể xử lý số âm ở dạng hex không?

Có thể, nhưng số âm sẽ được biểu diễn bằng dạng bù 2.
Ví dụ:

#include <stdio.h>

int main() {
    int negativeValue = -16;
    printf("Thập phân: %d -> Hex: %X
", negativeValue, negativeValue);
    return 0;
}

Kết quả FFFFFFF0 là dạng bù 2 của -16.

Q8: Cách khai báo mảng số hex trong C?

Có thể viết trực tiếp các giá trị hex.
Ví dụ:

#include <stdio.h>

int main() {
    unsigned char hexArray[] = {0x1A, 0x2B, 0x3C, 0x4D};

    for (int i = 0; i < sizeof(hexArray); i++) {
        printf("Mảng[%d]: 0x%X
", i, hexArray[i]);
    }
    return 0;
}

Kết quả:

Mảng[0]: 0x1A
Mảng[1]: 0x2B
Mảng[2]: 0x3C
Mảng[3]: 0x4D
侍エンジニア塾