0

Thực hiện hàm malloc() căng chỉnh địa chỉ trong C

#include <stdlib.h>
#include <stdint.h>
#include <errno.h>

int my_posix_memalign(void **memptr, size_t alignment, size_t size) {
    // --- 1. Kiểm tra đầu vào ---
    if (memptr == NULL)
        return EINVAL;

    if ((alignment & (alignment - 1)) != 0 || alignment < sizeof(void*))
        return EINVAL; // alignment phải là power-of-two và >= sizeof(void*)

    // --- 2. Cấp phát dư để có thể căn chỉnh ---
    void *original = malloc(size + alignment - 1 + sizeof(void*)); // hiện tại original chứa địa chỉ bắt đầu bất kì
    if (original == NULL)
        return ENOMEM;

    // --- 3. Tính địa chỉ căn chỉnh ---
    uintptr_t aligned = sizeof(void*) + ((uintptr_t)original + (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 *ptr) {
    if (ptr)
        free(((void**)ptr)[-1]);
}

int main(void) {
    void *ptr;
    size_t alignment = 64;
    size_t size = 128;

    int ret = my_posix_memalign(&ptr, alignment, size);
    if (ret != 0) {
        printf("Allocation failed, error = %d\n", ret);
        return -1;
    }

    printf("ptr = %p, alignment = %zu, ptr %% alignment = %zu\n",
           ptr, alignment, (size_t)ptr % alignment);

    my_posix_free(ptr);
    return 0;
}
 ((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.

image.png

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí