Thực hiện hàm malloc() căng chỉnh địa chỉ trong C
Bài viết này khó, đặc biệt nếu bạn chưa nắm vững kiến thức về con trỏ (pointer) và con trỏ kép (double-pointer).
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#define ALIGN 2
#define SIZE  20
int my_posix_memalign(void **memptr, size_t alignment, size_t size) {
    // --- 1. Kiểm tra đầu vào ---
    if (memptr == NULL)
        return EINVAL;
    // alignment phải là power-of-two và >= sizeof(void*)
    if ((alignment & (alignment - 1)) != 0) // || alignment < sizeof(void*)) - chỉ dùng trên hệ thống 32-bit, 64-bit
        return EINVAL; 
    // --- 2. Cấp phát dư để có thể căn chỉnh ---
    size_t total_size = size + (alignment - 1) + sizeof(void *);
    
    // hiện tại original chứa địa chỉ bắt đầu của vùng nhớ
    void *original = malloc(total_size); 
    if (original == NULL)
        return ENOMEM;
    // --- 3. Tính địa chỉ căn chỉnh ---
    uintptr_t start_addr = (uintptr_t)original + sizeof(void*);
    
    // Can chinh địa chỉ
    uintptr_t aligned = (start_addr + alignment - 1) & ~(alignment - 1);
    // --- 4. Lưu địa chỉ gốc ngay trước vùng aligned ---
    ((void**)aligned)[-1] = original;
    // --- 5. Gán cho con trỏ người dùng ---
    *memptr = (void*)aligned;
    return 0;
}
// --- 6. Hàm free tương ứng ---
void my_posix_free(void *original_p) {
    if (original_p)
        free(((void**)original_p)[-1]);
}
int main(void) {
  void *start_memory_align;
  printf("Địa chỉ của con trỏ start_memory_align: %p\n", &start_memory_align);
  int ret = my_posix_memalign(&start_memory_align, ALIGN, SIZE);
  
  // Địa chỉ gốc khi cấp phát bở malloc();
  void *original_malloc = ((void**)start_memory_align)[-1];
  printf("Địa chỉ original của memory: %p\n", original_malloc);
  
  // Địa chỉ bắt đầu này đã được căng chỉnh
  printf("\nĐịa chỉ bắt đầu memory: %p\n", start_memory_align);
  printf("Giá trị tại địa chỉ thứ 1 của memory: %d\n", *((uint8_t *)start_memory_align));
  printf("Gía trị tại địa chỉ thứ 2 của memory: %d\n", *((uint8_t *)start_memory_align + 1));
    
  // Xử lí nếu cấp phát không thành công
  if (ret != 0) {
    printf("Allocation failed, error = %d\n", ret);
    return -1;
  }
  my_posix_free(start_memory_align);
  return 0;
}
Đầu ra chương trình
Địa chỉ của con trỏ start_memory_align: 0x7ffecc7e5938
Địa chỉ original của memory: 0x5634b76652b0
Địa chỉ bắt đầu memory: 0x5634b76652b8
Giá trị tại địa chỉ thứ 1 của memory: 0
Gía trị tại địa chỉ thứ 2 của memory: 0

Giải thích
 ((void**)aligned)[-1] = original;
✅ Khi muốn “gán giá trị tại vị trí trước vùng aligned”, cần truy cập vào bộ nhớ tại địa chỉ đó như một mảng con trỏ.
Mà trong C, cú pháp mảng ptr[index] chỉ hợp lệ nếu ptr là một pointer tới một kiểu nào đó.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main() {
  int arr[] = {1, 2, 3};
  int *p = &arr[1];
  
  printf("gia tri cua p: %d\n", *p);
  p[-1] = 42; ✅
  
  printf("gia tri cua arr[0] = %d\n", arr[0]);
  
  return 0;
}
Đầu ra chương trình
gia tri cua p: 2
gia tri cua arr[0] = 42
Nhưng nếu tôi viết
void *p = &arr[1]; ❌ 
Đầu ra chương trình
Main.c: In function ‘main’:
Main.c:9:33: warning: dereferencing ‘void *’ pointer
    9 |   printf("gia tri cua p: %d\n", *p);
      |                                 ^~
Main.c:9:33: error: invalid use of void expression
Main.c:10:4: warning: dereferencing ‘void *’ pointer
   10 |   p[-1] = 42;
      |    ^
Main.c:10:9: error: invalid use of void expression
   10 |   p[-1] = 42;
      |         ^
=> Không hợp lệ, vì void không có kích thước, compiler không biết lùi lại bao nhiêu byte khi tôi viết p - 1.

Nên ta cần phải ép kiểu trước khi thực thi. Khi thực hiện (void**)aligned thì compiler sẽ hiểu aligned bây giờ được hiểu là một vùng nhớ có thể chứa các con trỏ (void*).
Và kích thước của mỗi con trỏ trên x86-64 là 8 bit, nên các phép toán số học sẽ được thực thi theo đơn vị là byte.
All rights reserved
 
  
 