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
.
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