원래 답변
{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
정답
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
요청 된 설명
첫 번째 단계는 만일을 위해 충분한 여유 공간을 할당하는 것입니다. 메모리는 16 바이트로 정렬되어야하므로 (앞의 바이트 주소는 16의 배수 여야 함을 의미 함) 16 바이트를 추가하면 충분한 공간이 확보됩니다. 처음 16 바이트 어딘가에 16 바이트 정렬 포인터가 있습니다. (주 malloc()
충분히 잘위한 정렬되는 포인터를 반환하도록되어 있는 . 목적을하지만, '모든'의 의미는 기본 유형 같은 것들에 대해 우선적으로 - long
, double
, long double
, long long
., 및 객체에 대한 포인터와 포인터 기능이있는 경우 그래픽 시스템을 사용하는 것과 같이보다 전문적인 작업을 수행하면 나머지 시스템보다 더 엄격한 정렬이 필요할 수 있습니다.
다음 단계는 void 포인터를 char 포인터로 변환하는 것입니다. GCC에도 불구하고, void 포인터에 대해 포인터 산술을 수행해서는 안됩니다 (GCC에는이를 악용 할 경우 알려주는 경고 옵션이 있습니다). 그런 다음 시작 포인터에 16을 추가하십시오. 가정은 malloc()
당신에게 엄청나게 잘못 정렬 된 포인터 : 0x800001를 반환했습니다. 16을 더하면 0x800011이됩니다. 이제 16 바이트 경계로 내림하고 싶습니다. 따라서 마지막 4 비트를 0으로 재설정하려고합니다. 0x0F에는 마지막 4 비트가 1로 설정되어 있습니다. 따라서 ~0x0F
마지막 4 개를 제외한 모든 비트가 1로 설정됩니다. 그리고 0x800011로 0x800010을 제공합니다. 다른 오프셋을 반복하고 동일한 산술이 작동하는지 확인할 수 있습니다.
마지막 단계는, free()
당신은 항상, 만, 복귀 : 쉽게 free()
값 그 중 하나 malloc()
, calloc()
또는 realloc()
당신에게 반환은 - 어떤 다른 재앙이다. mem
그 가치를 지키기 위해 올바르게 제공 하셨습니다. 감사합니다. 무료로 릴리스합니다.
마지막으로, 시스템 malloc
패키지 의 내부에 대해 알고 있다면 16 바이트로 정렬 된 데이터를 반환하거나 8 바이트로 정렬 될 수 있다고 추측 할 수 있습니다. 16 바이트로 정렬 된 경우 값을 적을 필요가 없습니다. 그러나 이것은 dodgy하고 이식 할 수 없습니다. 다른 malloc
패키지는 최소 정렬이 다르므로 다른 작업을 할 때 한 가지를 가정하면 코어 덤프가 발생합니다. 광범위한 한계 내에서이 솔루션은 이식 가능합니다.
posix_memalign()
정렬 된 메모리를 얻는 다른 방법으로 다른 사람이 언급 되었습니다. 모든 곳에서 사용할 수는 없지만 종종 이것을 기본으로 사용하여 구현할 수 있습니다. 정렬은 2의 거듭 제곱 인 것이 편리하다는 점에 유의하십시오. 다른 정렬은 더 지저분합니다.
한 번 더 주석-이 코드는 할당이 성공했는지 확인하지 않습니다.
개정
Windows Programmer 는 포인터에서 비트 마스크 작업을 수행 할 수 없으며 실제로 GCC (3.4.6 및 4.3.1 테스트)는 그렇게 불평한다고 지적했습니다. 따라서 기본 코드의 수정 된 버전 (기본 프로그램으로 변환)은 다음과 같습니다. 또한 지적했듯이 16 대신 15를 추가하는 자유를 얻었습니다. uintptr_t
C99는 대부분의 플랫폼에서 액세스 할 수있을 정도로 오래 지속되었으므로 사용 하고 있습니다. 명령문 PRIXPTR
에서 사용하지 않은 printf()
경우을 사용하는 #include <stdint.h>
대신 충분합니다 #include <inttypes.h>
. [이 코드에는 CR이 지적한 픽스가 포함되어 있는데 , 몇 년 전 Bill K 가 처음으로 작성한 시점을 되풀이하여 지금까지 간과 할 수있었습니다.]
#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);
}
그리고 여기에 조금 더 일반화 된 버전이 있습니다.이 버전은 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);
}
test_mask()
범용 할당 함수 로 변환하려면 할당 자로부터의 단일 반환 값이 여러 사람이 답변에 표시 한 것처럼 릴리스 주소를 인코딩해야합니다.
면접관 문제
Uri 는 다음과 같이 말했습니다. 아마도 오늘 아침에 독해 문제를 겪고있을 수도 있지만 인터뷰 질문에 구체적으로 "어떻게 1024 바이트의 메모리를 할당하겠습니까?"라고 말하면 분명히 그 이상을 할당하게됩니다. 면접관의 자동 실패가 아닌가?
내 답변이 300 자 댓글에 맞지 않습니다 ...
그것은 달려 있다고 생각합니다. 저를 포함한 대부분의 사람들은 "1024 바이트의 데이터를 저장할 수있는 공간과 기본 주소가 16 바이트의 배수 인 공간을 어떻게 할당하겠습니까?" 면접관이 실제로 1024 바이트를 할당하고 16 바이트를 정렬 할 수있는 방법을 의미한다면 옵션이 더 제한적입니다.
- 분명히 한 가지 가능성은 1024 바이트를 할당 한 다음 해당 주소에 '정렬 처리'를 제공하는 것입니다. 이 방법의 문제점은 실제 사용 가능한 공간이 올바르게 결정되지 않는다는 것입니다 (사용 가능한 공간은 1008에서 1024 바이트 사이이지만 어떤 크기를 지정할 수있는 메커니즘이 없었기 때문에).
- 또 다른 가능성은 전체 메모리 할당자를 작성하고 반환하는 1024 바이트 블록이 적절하게 정렬되어 있어야한다는 것입니다. 이 경우 제안 된 솔루션과 상당히 유사한 작업을 수행 할 수 있지만 할당 기 내부에 숨길 수 있습니다.
그러나 면접관이 이러한 응답 중 하나를 예상 한 경우이 솔루션이 밀접하게 관련된 질문에 답변한다는 것을 인식 한 다음 대화를 올바른 방향으로 가리 키도록 질문을 재구성 할 것으로 기대합니다. (따라서 면접관이 정말로 비참한 사람이라면 그 일을 원하지 않을 것입니다. 충분히 정확한 요구 사항에 대한 답변이 수정없이 화염에 빠지면 면접관은 일하기에 안전한 사람이 아닙니다.)
세상은 계속 움직입니다
질문 제목이 최근에 변경되었습니다. 그것은이었다 난처한 상황에 빠진 날 C 인터뷰 질문의 메모리 정렬을 해결 . 수정 된 제목 ( 표준 라이브러리 만 사용하여 정렬 된 메모리를 할당하는 방법? )에는 약간 수정 된 답변이 필요합니다.이 부록은이를 제공합니다.
C11 (ISO / IEC 9899 : 2011) 기능 추가 aligned_alloc()
:
7.22.3.1 aligned_alloc
기능
개요
#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);
설명
이 aligned_alloc
함수는에 의해 정렬이 지정되고에 의해 alignment
크기가 지정되고 size
값이 결정되지 않은 객체에 공간을 할당합니다 . 의 값은 alignment
구현에 의해 지원되는 유효한 정렬이어야하고의 값은 size
의 정수배 여야합니다 alignment
.
반환 함수가 반환 널 포인터 또는 할당 된 공간에 대한 포인터 중 하나를.
aligned_alloc
그리고 POSIX는 posix_memalign()
다음을 정의합니다 .
#include <stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size);
기술
이 posix_memalign()
함수는로 size
지정된 경계에 정렬 된 바이트 alignment
를 할당하고에 할당 된 메모리에 대한 포인터를 반환합니다 memptr
. 의 값은 alignment
2의 배수입니다 sizeof(void *)
.
성공적으로 완료되면에 의해 지정된 값은 memptr
의 배수입니다 alignment
.
요청 된 공간의 크기가 0이면 동작이 구현 정의됩니다. 리턴 된 값 memptr
은 널 포인터 또는 고유 포인터 여야합니다.
이 free()
함수는 이전에 할당 한 메모리를 할당 해제해야합니다 posix_memalign()
.
반품 가치
성공적으로 완료되면 posix_memalign()
0을 반환합니다. 그렇지 않으면 오류를 나타 내기 위해 오류 번호가 반환됩니다.
이 두 가지 중 하나 또는 둘 다를 사용하여 지금 질문에 대답 할 수 있지만 질문에 처음 대답했을 때는 POSIX 기능 만 옵션이었습니다.
배후에서 새로운 정렬 메모리 기능은 정렬을보다 쉽게 강제하고 코드가 작동하지 않도록 정렬 메모리의 시작을 내부적으로 추적하는 기능을 제외하고는 질문에 설명 된 것과 거의 동일한 작업을 수행합니다. 특별히 처리해야합니다 – 사용 된 할당 함수에 의해 반환 된 메모리를 해제합니다.