Jawaban asli
{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
Jawaban tetap
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
Penjelasan seperti yang diminta
Langkah pertama adalah mengalokasikan ruang cadangan yang cukup, untuk berjaga-jaga. Karena memori harus selaras 16-byte (artinya alamat byte utama harus kelipatan 16), menambahkan 16 byte tambahan menjamin bahwa kami memiliki cukup ruang. Di suatu tempat di 16 byte pertama, ada pointer 16 byte yang disejajarkan. (Perhatikan bahwa malloc()
seharusnya mengembalikan pointer yang cukup baik selaras untuk setiap . Tujuan Namun, arti dari 'setiap' terutama untuk hal-hal seperti dasar jenis - long
, double
, long double
, long long
., Dan pointer ke objek dan pointer ke fungsi Bila Anda melakukan hal-hal yang lebih khusus, seperti bermain dengan sistem grafis, mereka dapat membutuhkan lebih banyak keselarasan yang ketat daripada sistem lainnya - karenanya pertanyaan dan jawaban seperti ini.)
Langkah selanjutnya adalah mengonversi void pointer ke char pointer; Meskipun demikian, GCC, Anda tidak seharusnya melakukan aritmatika penunjuk pada pointer kosong (dan GCC memiliki opsi peringatan untuk memberi tahu Anda ketika Anda menyalahgunakannya). Kemudian tambahkan 16 ke pointer mulai. Misalkan malloc()
mengembalikan Anda pointer yang sangat tidak selaras: 0x800001. Menambahkan 16 memberi 0x800011. Sekarang saya ingin membulatkan batas 16-byte - jadi saya ingin mengatur ulang 4 bit terakhir ke 0. 0x0F memiliki 4 bit terakhir yang disetel menjadi satu; oleh karena itu, ~0x0F
tetapkan semua bit ke satu kecuali empat terakhir. Anding itu dengan 0x800011 memberi 0x800010. Anda dapat beralih dari offset lain dan melihat bahwa aritmatika yang sama berfungsi.
Langkah terakhir,, free()
mudah: Anda selalu, dan hanya, kembali ke free()
nilai yang salah satunya malloc()
, calloc()
atau realloc()
kembali kepada Anda - hal lain adalah bencana. Anda menyediakan dengan benar mem
untuk memegang nilai itu - terima kasih. Gratis merilisnya.
Akhirnya, jika Anda tahu tentang internal malloc
paket sistem Anda, Anda bisa menebak bahwa itu mungkin mengembalikan data sejajar 16-byte (atau mungkin sejajar 8-byte). Jika sejajar 16 byte, maka Anda tidak perlu bingung dengan nilai-nilai tersebut. Namun, ini cerdik dan non-portabel - malloc
paket lain memiliki keberpihakan minimum yang berbeda, dan oleh karena itu mengasumsikan satu hal ketika melakukan sesuatu yang berbeda akan menyebabkan dump inti. Dalam batas luas, solusi ini portabel.
Orang lain disebut posix_memalign()
sebagai cara lain untuk mendapatkan memori yang selaras; yang tidak tersedia di mana-mana, tetapi seringkali dapat diimplementasikan menggunakan ini sebagai dasar. Perhatikan bahwa itu nyaman bahwa perataan adalah kekuatan 2; keberpihakan lainnya berantakan.
Satu komentar lagi - kode ini tidak memeriksa apakah alokasi berhasil.
Amandemen
Windows Programmer menunjukkan bahwa Anda tidak dapat melakukan operasi topeng bit pada pointer, dan, memang, GCC (3.4.6 dan 4.3.1 diuji) tidak mengeluh seperti itu. Jadi, versi kode dasar yang diubah - diubah menjadi program utama, mengikuti. Saya juga mengambil kebebasan untuk menambahkan hanya 15 bukan 16, seperti yang telah ditunjukkan. Saya menggunakan uintptr_t
karena C99 sudah ada cukup lama untuk dapat diakses di sebagian besar platform. Jika bukan karena penggunaan PRIXPTR
dalam printf()
pernyataan, itu sudah cukup untuk #include <stdint.h>
bukan menggunakan #include <inttypes.h>
. [Kode ini termasuk perbaikan yang ditunjukkan oleh CR , yang mengulangi poin yang pertama kali dibuat oleh Bill K beberapa tahun yang lalu, yang berhasil saya abaikan sampai sekarang.]
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
int main(void)
{
void *mem = malloc(1024+15);
void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
return(0);
}
Dan ini adalah versi yang sedikit lebih umum, yang akan bekerja untuk ukuran yang merupakan kekuatan 2:
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
static void test_mask(size_t align)
{
uintptr_t mask = ~(uintptr_t)(align - 1);
void *mem = malloc(1024+align-1);
void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
assert((align & (align - 1)) == 0);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
}
int main(void)
{
test_mask(16);
test_mask(32);
test_mask(64);
test_mask(128);
return(0);
}
Untuk mengkonversi test_mask()
ke fungsi alokasi tujuan umum, nilai pengembalian tunggal dari pengalokasi harus meng-encode alamat rilis, seperti yang ditunjukkan beberapa orang dalam jawaban mereka.
Masalah dengan pewawancara
Uri berkomentar: Mungkin saya mengalami masalah pemahaman membaca pagi ini, tetapi jika pertanyaan wawancara secara khusus mengatakan: "Bagaimana Anda mengalokasikan 1024 byte memori" dan Anda jelas mengalokasikan lebih dari itu. Bukankah itu merupakan kegagalan otomatis dari pewawancara?
Respons saya tidak akan cocok dengan komentar 300 karakter ...
Tergantung, kurasa. Saya pikir kebanyakan orang (termasuk saya) mengambil pertanyaan yang berarti "Bagaimana Anda mengalokasikan ruang di mana 1024 byte data dapat disimpan, dan di mana alamat basis adalah kelipatan 16 byte". Jika pewawancara benar-benar bermaksud bagaimana Anda dapat mengalokasikan 1024 byte (hanya) dan membuatnya sejajar 16-byte, maka opsi lebih terbatas.
- Jelas, satu kemungkinan adalah mengalokasikan 1024 byte dan kemudian memberikan alamat itu 'perlakuan penyelarasan'; masalah dengan pendekatan itu adalah bahwa ruang yang tersedia sebenarnya tidak ditentukan dengan benar (ruang yang dapat digunakan adalah antara 1008 dan 1024 byte, tetapi tidak ada mekanisme yang tersedia untuk menentukan ukuran mana), yang menjadikannya kurang bermanfaat.
- Kemungkinan lain adalah bahwa Anda diharapkan untuk menulis alokasi memori penuh dan memastikan bahwa blok 1024-byte yang Anda kembalikan selaras. Jika itu masalahnya, Anda mungkin akhirnya melakukan operasi yang cukup mirip dengan apa yang dilakukan solusi yang diusulkan, tetapi Anda menyembunyikannya di dalam pengalokasi.
Namun, jika pewawancara mengharapkan salah satu dari tanggapan tersebut, saya berharap mereka mengenali bahwa solusi ini menjawab pertanyaan yang terkait erat, dan kemudian membingkai ulang pertanyaan mereka untuk mengarahkan percakapan ke arah yang benar. (Lebih jauh, jika pewawancara benar-benar stroppy, maka saya tidak akan menginginkan pekerjaan itu; jika jawaban terhadap persyaratan yang tidak tepat akurat ditembak jatuh dalam api tanpa koreksi, maka pewawancara bukanlah seseorang yang aman untuk bekerja.)
Dunia bergerak
Judul pertanyaan telah berubah baru-baru ini. Itu Memecahkan keselarasan memori di C pertanyaan wawancara yang bingung saya . Judul yang direvisi ( Bagaimana cara mengalokasikan memori selaras hanya menggunakan perpustakaan standar? ) Menuntut jawaban yang sedikit direvisi - adendum ini menyediakannya.
C11 (ISO / IEC 9899: 2011) menambahkan fungsi aligned_alloc()
:
7.22.3.1 aligned_alloc
Fungsi
Ringkasan
#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);
Deskripsi
The aligned_alloc
mengalokasikan fungsi ruang untuk obyek yang keselarasan ditentukan oleh alignment
, yang ukurannya ditentukan oleh size
, dan yang nilainya tak tentu. Nilai alignment
harus merupakan keselarasan yang valid yang didukung oleh implementasi dan nilai size
harus merupakan kelipatan integral dari alignment
.
Pengembalian
tersebut aligned_alloc
kembali fungsi baik pointer nol atau pointer ke ruang yang dialokasikan.
Dan POSIX mendefinisikan posix_memalign()
:
#include <stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size);
DESKRIPSI
The posix_memalign()
Fungsi akan mengalokasikan size
byte selaras pada batas yang ditentukan oleh alignment
, dan akan kembali pointer ke memori yang dialokasikan di memptr
. Nilai alignment
harus menjadi kekuatan dua kelipatan sizeof(void *)
.
Setelah berhasil diselesaikan, nilai yang ditunjukkan oleh memptr
harus kelipatan alignment
.
Jika ukuran ruang yang diminta adalah 0, perilaku ditentukan oleh implementasi; nilai yang dikembalikan memptr
harus berupa pointer nol atau pointer unik.
The free()
Fungsi akan deallocate memori yang sebelumnya telah dialokasikan oleh posix_memalign()
.
NILAI KEMBALI
Setelah berhasil diselesaikan, posix_memalign()
akan mengembalikan nol; jika tidak, nomor kesalahan harus dikembalikan untuk menunjukkan kesalahan.
Salah satu atau keduanya dapat digunakan untuk menjawab pertanyaan sekarang, tetapi hanya fungsi POSIX yang menjadi pilihan ketika pertanyaan itu awalnya dijawab.
Di belakang layar, fungsi memori selaras baru melakukan pekerjaan yang sama seperti yang dijelaskan dalam pertanyaan, kecuali mereka memiliki kemampuan untuk memaksa penyelarasan lebih mudah, dan melacak dimulainya memori selaras secara internal sehingga kode tidak harus berurusan dengan khusus - itu hanya membebaskan memori yang dikembalikan oleh fungsi alokasi yang digunakan.