표준 라이브러리를 사용하여 정렬된 메모리만 할당하는 방법
나는 방금 취업 면접의 일환으로 시험을 끝냈는데, 한 문제가 나를 난처하게 만들었어. 심지어 참고용으로 구글을 사용했거든.StackOverflow 승무원이 이를 통해 무엇을 할 수 있는지 보고 싶다.
그
memset_16aligned
함수에 16바이트 정렬 포인터가 전달되지 않으면 충돌한다.a) 1024바이트의 메모리를 어떻게 할당하여 16바이트의 경계로 정렬하시겠습니까?
b) 다음 시간 이후에 메모리를 확보하십시오.memset_16aligned
처형당했어
{
void *mem;
void *ptr;
// answer a) here
memset_16aligned(ptr, 0, 1024);
// answer b) here
}
오리지널 해답
{
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()
어떤 목적으로든 충분히 정렬된 포인터를 반환해야 한다.그러나, 'anything'의 의미는 주로 기본 유형과 같은 것에 대한 것이다.long
double
long double
long long
, 객체에 대한 포인터 및 함수에 대한 포인터.그래픽 시스템을 사용하는 것과 같이 좀 더 전문적인 작업을 할 때, 그래픽 시스템은 나머지 시스템보다 더 엄격한 정렬이 필요할 수 있다. 따라서 질문과 답변은 다음과 같다.)
다음 단계는 보이드 포인터를 문자 포인터로 변환하는 것이다. GCC는 그럼에도 불구하고 보이드 포인터에서 포인터 산술을 수행해서는 안 된다(그리고 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바이트 정렬이었다면, 당신은 그 값을 곱씹을 필요가 없었을 것이다.하지만, 이것은 위험하고 휴대할 수 없다 - 다른 것.malloc
패키지는 서로 다른 최소 정렬을 가지고 있으며, 따라서 다른 작업을 수행할 때 한 가지를 가정하면 코어 덤프가 발생할 수 있다.넓은 범위 내에서 이 솔루션은 휴대할 수 있다.
다른 분이 언급하셨습니다.posix_memalign()
정렬된 메모리를 얻기 위한 또 다른 방법으로, 그것은 어디에서나 사용할 수 없지만 종종 이것을 기초로 하여 구현될 수 있다.정렬의 검정력이 2인 것이 편리하다는 점에 유의하십시오. 다른 정렬은 더 지저분하다.
한 가지 더 설명 - 이 코드는 할당이 성공했는지 확인하지 않는다.
수정안
윈도 프로그래머는 포인터에 비트 마스크 연산을 할 수 없다고 지적했고, 실제로 GCC(3.4.6, 4.3.1 테스트)는 그렇게 불평한다.따라서 기본 코드의 개정판(기본 프로그램으로 변환됨)은 다음과 같다.나는 또한 지적된 바와 같이 16이 아닌 15개만 추가하는 자유를 누렸다.사용하고 있다.uintptr_t
C99는 대부분의 플랫폼에서 접근할 수 있을 만큼 충분히 길었기 때문에.의 용도가 아니었다면PRIXPTR
에서printf()
에 대한 진술로 충분할 것이다.#include <stdint.h>
사용하지 않고#include <inttypes.h>
. [이 코드는 C.R이 지적한 수정안을 포함하고 있는데, 몇 년 전에 빌 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: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()
일반적인 목적 할당 함수로, 할당자의 단일 반환 값은 여러 사람이 답변에서 표시한 대로 릴리스 주소를 인코딩해야 할 것이다.
면접관 문제
우리당은 다음과 같이 논평했다.오늘 아침에 독해력 문제가 생겼을 수도 있지만, 면접 질문에서 구체적으로 "1024바이트의 메모리를 어떻게 할당하시겠습니까"라고 말하면, 그 이상의 메모리를 명확하게 할당한다.면접관의 자동실패가 되지 않을까?
내 답변은 300자 코멘트에 맞지 않아...
내 생각에 그건 다른 것 같아.대부분의 사람들(나 포함)은 이 질문을 "1024바이트의 데이터를 저장할 수 있는 공간과 16바이트의 기본 주소가 배수인 공간을 어떻게 할당하시겠습니까"라는 뜻으로 받아들였다고 생각한다.면접관이 정말로 1024바이트(전용)를 할당하고 16바이트를 정렬할 수 있는 방법을 의미했다면, 그 옵션은 더 제한적이다.
- 분명히, 한 가지 가능성은 1024바이트를 할당하고 나서 '정렬 처리'를 하는 것이다. 그 접근방식의 문제는 실제 가용 공간이 적절하게 결정되지 않고 있다는 것이다(사용 가능한 공간이 1008바이트에서 1024바이트 사이지만 어떤 크기를 명시할 수 있는 메커니즘이 없었다). 이것은 유용하지 않게 만든다.
- 또 다른 가능성은 전체 메모리 할당자를 작성하고 반환하는 1024바이트 블록이 적절하게 정렬되었는지 확인하는 것이다.만약 그렇다면, 당신은 아마도 제안된 솔루션이 했던 것과 상당히 유사한 작업을 하게 될 것이다. 그러나 당신은 그것을 할당자 안에 숨기는 것이다.
그러나, 면접관이 어느 쪽이든 그 대답 중 하나를 기대했다면, 나는 그들이 이 해답이 밀접하게 관련된 질문에 답한다는 것을 인식한 다음, 그들의 질문을 재구성하여 대화를 올바른 방향으로 이끌어 내기를 기대한다.(더구나, 면접관이 정말로 지루해 한다면, 나는 그 일을 원하지 않을 것이다; 만약 면접관이 제대로 진척되지 않았다면, 나는 그 일을 하고 싶지 않을 것이다; 만약 불충분한 질문에 대한 대답이 나온다면.recise 요건은 수정 없이 화염에 휩싸여 격추된다. 그러면 인터뷰 진행자는 작업하기에 안전한 사람이 아니다.)
세상은 앞으로 나아간다.
그 문제의 제목은 최근에 바뀌었다.나를 난처하게 만든 것은 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
함수는 할당된 공간에 null 포인터 또는 포인터를 반환한다.
그리고 POSIX는 다음을 정의한다.
#include <stdlib.h> int posix_memalign(void **memptr, size_t alignment, size_t size);
설명
그
posix_memalign()
함수는 할당되어야 한다.size
지정된 경계에서 정렬된 바이트 수:alignment
, 그리고 포인터를 의 할당된 메모리에 반환해야 한다.memptr
의 .alignment
다음 중 두 배수의 검정력이어야 한다.sizeof(void *)
.성공적으로 완료되었을 때, 다음이 가리키는 값
memptr
의 배수여야 한다.alignment
.된 이면 그
memptr
null 포인터 또는 고유한 포인터여야 한다.그
free()
함수는 이전에 할당한 메모리를 할당 해제해야 한다.posix_memalign()
.반환 값
성공적으로 완성되면,
posix_memalign()
0을 반환해야 한다. 그렇지 않으면 오류를 나타내는 오류 번호를 반환해야 한다.
이 둘 중 하나 또는 둘 다 지금 질문에 답하기 위해 사용할 수 있지만, 원래 질문에 답할 때는 POSIX 기능만 옵션이었다.
뒤에서 새로운 정렬된 메모리 기능은 보다 쉽게 정렬을 강요할 수 있고, 코드가 특별히 다루지 않아도 되도록 내부적으로 정렬된 메모리의 시작을 추적할 수 있다는 점만 제외하면, 질문에 설명된 것과 거의 동일한 작업을 수행한다. 다만, 새로운 정렬된 메모리 기능은 이러한 할당 기능으로 반환된 메모리를 자유롭게 한다.사용된
질문을 어떻게 보느냐에 따라 약간 다른 세 가지 답변:
1) 정확한 질문에 충분한 답은 조나단 레플러의 해결책이며, 16 정렬까지 반올림하기 위해서는 16 바이트가 아니라 15 바이트만 더 있으면 된다는 점만 빼면 된다.
A:
/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;
B:
free(mem);
2) 좀 더 일반적인 메모리 할당 기능을 위해 발신자는 두 개의 포인터(사용할 포인트와 사용할 포인트, 사용할 포인트와 사용할 포인트에서 자유 포인트)를 추적하지 않아도 된다.따라서 정렬된 버퍼 아래에 '실제' 버퍼에 포인터를 저장하십시오.
A:
void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;
B:
if (ptr) free(((void**)ptr)[-1]);
밈에 15바이트만 추가된 (1)과 달리, 이 코드는 당신의 구현이 말로크로부터 32바이트 정렬을 보증하기 위해 일어난다면 실제로 정렬을 줄일 수 있다는 점에 유의한다(비슷하지만 이론상 C 구현은 32바이트 정렬 유형을 가질 수 있다).당신이 하는 일이 memset_16 arligned라고만 불러도 상관없지만, 만약 당신이 그 메모리를 구조체에 사용한다면 그것은 문제가 될 수 있다.
구현별 맞춤 보증이 무엇인지 프로그램적으로 판단할 수 있는 방법이 없기 때문에 이를 위한 좋은 해결책이 무엇인지 즉석에서 확신할 수 없다(사용자에게 반환되는 버퍼는 임의의 구조체에 반드시 적합한 것이 아니라는 것을 경고하는 것 이외에는).시작 시 두 개 이상의 1바이트 버퍼를 할당할 수 있고, 가장 나쁜 정렬은 보장된 정렬이라고 가정할 수 있다.틀렸다면 기억을 낭비하는 거야더 좋은 생각 있는 사람 있으면 그렇게 말해줘...
[더하기: '표준' 수법은 '최대한 정렬된 유형'의 조합을 만들어 필요한 정렬을 결정하는 것이다.최대 정렬 유형은 (C99에서) '일 가능성이 있다.long long
', 'long double
', 'void *
' 또는 'void (*)(void)
'; 포함하면<stdint.h>
, 아마도 '을 사용할 수 있을 것이다.intmax_t
… 대신에long long
(그리고, 파워 6 (AIX) 기계에서는,intmax_t
128비트 정수 유형).이 결합에 대한 정렬 요건은 단일 문자 뒤에 결합이 있는 구조물에 결합하여 결정할 수 있다.
struct alignment
{
char c;
union
{
intmax_t imax;
long double ldbl;
void *vptr;
void (*fptr)(void);
} u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;
중 큰 맞춤(예: 16)과 그 모양(예: 16)을 한다.align
위에서 계산한 값
에서는 (64비트) Solaris 10 에서 결과에 대한 기본 .malloc()
32바이트의 배수다.
]
실제로 정렬된 할당자는 정렬에 대해 유선 연결보다는 매개 변수를 사용하는 경우가 많다.따라서 사용자는 자신이 아끼는 구조물의 크기(또는 그것보다 크거나 같은 최소 전력 2개)를 통과하고 모든 것이 잘 될 것이다.
3) 플랫폼에서 제공하는 기능 사용:posix_memalign
POSIX의 경우를 _aligned_malloc
윈도서.
4) C11을 사용할 경우 가장 깔끔하고 휴대성이 뛰어나며 간결한 옵션은 이 버전의 언어사양서에 소개된 표준 라이브러리 기능을 사용하는 것이다.
물론 POSIX 플랫폼에서도 시도해 볼 수 있다.
불행하게도, C99에 부합하는 C 구현을 통해 이동 가능한 방식으로 C99의 정렬을 보장하는 것은 매우 어려워 보인다. 그 이유는?포인터는 평평한 메모리 모델에서 상상할 수 있는 "바이트 어드레스"가 될 것이라고 보장되지 않기 때문이다.어차피 선택형인 uintptr_t의 표현도 그렇게 보장되지 않는다.
우리는 보이드에 대한 표현을 사용하는 몇 가지 구현에 대해 알 수 있을 것이다. 이것은 단순한 바이트 주소지만, C99에 의해 프로그래머인 우리에게 불투명하다.구현은 오프셋이 "실제에서의 정렬"을 누구-알 수 있는지 알 수 있는 {segment, offset} 집합에 의한 포인터를 나타낼 수 있다.왜, 포인터는 해시 테이블 조회 값의 어떤 형태일 수도 있고, 링크된 목록 조회 값일 수도 있다.그것은 경계 정보를 암호화할 수 있다.
최근 C Standard의 C1X 초안에서는 _Alignas 키워드를 볼 수 있다.그게 좀 도움이 될 것 같아.
C99가 우리에게 주는 유일한 보장은 메모리 할당 기능이 어떤 개체 유형을 가리키는 포인터에 할당하기에 적합한 포인터를 반환한다는 것이다.객체 정렬을 특정할 수 없기 때문에, 우리는 잘 정의되고 휴대 가능한 방식으로 정렬을 책임지는 우리 자신의 할당 기능을 구현할 수 없다.
이 주장에 대해 틀리면 좋을 것이다.
아마도 그들은 memalign에 대한 지식으로 만족했을까?그리고 조나단 레플러가 지적한 바와 같이, 알아야 할 두 가지 새로운 선호 기능이 있다.
아이고, 플로린이 날 때렸네.하지만 내가 링크한 맨 페이지를 읽어보면, 이전 포스터에서 제공한 예시를 이해할 수 있을 것이다.
memalign, Aligned-Memory-Blocks를 사용하는 것이 이 문제에 대한 좋은 해결책이 될 수 있다.
여기 '반올림' 부분에 대한 대체적인 접근법이 있다.가장 훌륭하게 코드화된 솔루션은 아니지만 작업을 수행하며, 이러한 유형의 구문은 기억하기가 좀 더 쉽다(2의 검정력이 아닌 정렬 값에도 사용 가능).그uintptr_t
깁스는 컴파일러를 달래기 위해 필요했다; 포인터 산술은 분할이나 곱셈을 별로 좋아하지 않는다.
void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
16 대 15 바이트 카운트 패딩 전면에서 N의 정렬을 얻기 위해 추가해야 하는 실제 숫자는 최대(0,N-M)이며 여기서 M은 메모리 할당자의 자연 정렬(둘 다 2의 힘)이다.
할당자의 최소 메모리 정렬은 1바이트이므로, 15=max(0,16-1)는 보수적인 대답이다.그러나 메모리 할당자가 32비트 인트라인이 정렬된 주소(일반적으로 사용됨)를 제공할 것임을 알고 있다면 12를 패드로 사용할 수 있었을 것이다.
이 예에서는 중요하지 않지만 저장된 모든 인트가 계산되는 12K RAM이 포함된 임베디드 시스템에서는 중요할 수 있다.
가능한 모든 바이트를 저장하려면 매크로로 구현하여 기본 메모리 정렬을 제공하십시오.다시 말하지만, 이것은 모든 바이트를 저장해야 하는 임베디드 시스템에만 유용할 것이다.
아래 예에서 대부분의 시스템에서 값 1은 다음과 같다.MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
그러나 32비트 정렬 할당을 사용하는 이론적 내장 시스템의 경우 다음과 같은 방법으로 약간의 귀중한 메모리를 절약할 수 있다.
#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT 4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
long add;
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);
size =1024;
alignment = 16;
aligned_size = size +(alignment -(size % alignment));
mem = malloc(aligned_size);
memset_16aligned(mem, 0, 1024);
free(mem);
이것이 가장 간단한 구현이길 바라며, 의견을 알려줘.
우리는 이러한 종류의 작업을 항상 수행하는데, 엑셀러레이터.프레임워크(Accelerate.framework)는, 항상 정렬에 주의를 기울여야 하는, 상당히 벡터화된 OS X/iOS 라이브러리 입니다.위에 언급되지 않은 한두 가지 옵션이 꽤 있다.
이런 작은 배열을 위한 가장 빠른 방법은 그것을 스택에 붙이는 것이다.GCC / 땡땡이 사용:
void my_func( void )
{
uint8_t array[1024] __attribute__ ((aligned(16)));
...
}
무료()는 필요 없다.이는 일반적으로 스택 포인터에서 1024를 뺀 다음 -alignment를 사용하여 스택 포인터로 이동하는 두 가지 지침이다.아마도 요청자는 어레이의 수명이 스택을 초과했거나 재귀가 작업 중이거나 스택 공간이 심각한 수준이기 때문에 힙에 대한 데이터가 필요했을 것이다.
OS X/iOS에서 malloc/calloc/etc에 대한 모든 통화는 항상 16바이트 정렬되어 있다.예를 들어, AVX용으로 정렬된 32바이트가 필요한 경우 posix_memalign을 사용하십시오.
void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
RunInCirclesWaivingArmsWildly();
...
free(buf);
몇몇 사람들은 비슷하게 작동하는 C++ 인터페이스를 언급했다.
페이지가 2의 큰 힘에 맞춰 정렬되므로 페이지 정렬 버퍼도 16바이트 정렬된다는 사실을 잊어서는 안 된다.따라서 mmap()와 valloc() 및 기타 유사한 인터페이스도 옵션이다. mmap()는 당신이 원한다면 0이 아닌 것으로 미리 초기화된 버퍼로 할당할 수 있다는 장점이 있다.페이지 정렬 크기가 있기 때문에 이 크기에서 최소 할당량을 얻을 수 없으며, 이 크기를 처음 터치할 때 VM 오류가 발생할 수 있다.
치지: 가드 말록이나 비슷한 것을 켜라.VM은 오버런을 포착하는 데 사용되고 경계가 페이지 경계에 있기 때문에 이 버퍼와 같이 n*16바이트 크기의 버퍼는 n*16바이트 정렬이 된다.
일부 Accelerate.framework 기능은 스크래치 공간으로 사용하기 위해 사용자가 제공한 온도 버퍼를 사용한다.여기서 우리는 우리에게 전달된 완충기가 심하게 잘못 정렬되어 있고 사용자가 우리의 삶을 악의에서 벗어나도록 적극적으로 노력하고 있다고 가정해야 한다. (우리의 테스트 케이스는 온도 완충기 바로 앞과 뒤에 가드 페이지를 붙여 독을 강조한다.)여기서 16바이트 정렬 세그먼트를 보증하는 데 필요한 최소 크기를 반환하고 그 후에 버퍼를 수동으로 정렬하십시오.이 크기는 원하는 크기_size + alignment - 1. 따라서 이 경우 1024 + 16 - 1 = 1039 바이트.그런 다음 다음과 같이 정렬하십시오.
#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
uint8_t *alignedBuf = (uint8_t*)
(((uintptr_t) tempBuf + ((uintptr_t)alignment-1))
& -((uintptr_t) alignment));
...
}
alignment-1을 추가하면 포인터가 첫 번째 정렬된 주소를 지나가고 -alignment(예: alignment=16의 경우 0xfff...ff0)를 사용하여 포인터가 다시 정렬된 주소로 이동된다.
다른 게시물에서 설명한 것처럼, 16바이트 정렬 보장이 없는 다른 운영 체제에서는 더 큰 크기로 말로크를 호출하고 나중에 포인터를 무료로 한쪽으로 설정한 다음, 위에서 설명한 대로 바로 정렬하고 우리의 임시 버퍼 케이스에 대해 설명한 대로 정렬된 포인터를 사용할 수 있다.
adjusted_memset에 대해서는, 이것은 다소 어리석은 것이다.정렬된 주소에 도달하려면 최대 15바이트까지 반복한 후 마지막에 가능한 정리 코드를 사용하여 정렬된 스토어로 진행하십시오.정렬된 영역을 겹치는 정렬되지 않은 스토어(최소한 벡터 길이일 경우) 또는 movmaskdqu와 같은 것을 사용하여 벡터 코드의 정리 비트도 수행할 수 있다.누군가 게으름을 피우고 있을 뿐이다.그러나, 면접관이 당신이 stdint.h, 비트 연산자, 기억력 기초에 익숙한지 알고 싶다면, 그래서 조작된 예는 용서받을 수 있다.
나는 아무도 표준 C99에서 요청한 것을 할 수 없다는 샤오의 대답을 투표로 올리지 않은 것이 놀랍다. 왜냐하면 포인터를 정식적으로 통합형으로 변환하는 것은 정의되지 않은 행동이기 때문이다.(표준에서 벗어나서 변환 가능uintptr_t
<->void*
그러나 표준은 그 어떤 조작도 허용하지 않는 것 같다.uintptr_t
값을 입력한 다음 다시 변환하십시오.)
이 질문을 읽을 때 가장 먼저 머리에 떠오른 것은 정렬된 구조를 정의하고 인스턴스화시킨 다음 그것을 가리키는 것이었다.
아무도 이런 제안을 하지 않았는데 내가 빠진 근본적인 이유가 있을까?
사이드노트로서, 차자의 배열을 사용했기 때문에 (시스템 차자가 8비트(즉, 1바이트)라고 가정하면), 나는 그 필요가 없다고 본다.__attribute__((packed))
필연적으로 (잘못되면 바로잡아준다) 하지만 어쨌든 집어넣었다.
이것은 내가 사용해 본 두 개의 시스템에서 작동하지만, 코드의 효능과 비교해서 나에게 잘못된 긍정을 주는 것을 모르는 컴파일러 최적화가 있을 수 있다.나는 사용했다.gcc 4.9.2
와 OSX에서gcc 5.2.1
우분투로
#include <stdio.h>
#include <stdlib.h>
int main ()
{
void *mem;
void *ptr;
// answer a) here
struct __attribute__((packed)) s_CozyMem {
char acSpace[16];
};
mem = malloc(sizeof(struct s_CozyMem));
ptr = mem;
// memset_16aligned(ptr, 0, 1024);
// Check if it's aligned
if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
else printf("Rubbish.\n");
// answer b) here
free(mem);
return 1;
}
MacOS X별:
- 말록과 함께 할당된 모든 포인터는 16바이트 정렬이다.
C11이 지원되므로 contined_malloc(16, size)로 전화하면 된다.
MacOS X는 memset, memcpy, memmove의 부팅 시간에 개별 프로세서에 최적화된 코드를 선택하며, 그 코드는 여러분이 들어본 적이 없는 트릭을 사용하여 빠르게 만든다.memset이 손으로 쓴 memset16보다 더 빨리 달릴 확률이 99%로 전체 질문을 무의미하게 만든다.
100% 휴대용 솔루션을 원한다면 C11 이전에는 없다.왜냐하면 포인터의 정렬 상태를 테스트할 수 있는 휴대용 방법이 없기 때문이다.100% 휴대할 필요가 없는 경우 사용 가능
char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;
이는 포인터를 서명되지 않은 int로 변환할 때 포인터의 정렬이 가장 낮은 비트에 저장된다고 가정한다.서명되지 않은 int로 변환하는 것은 정보가 손실되고 구현이 정의되지만, 결과를 다시 포인터로 변환하지 않기 때문에 중요하지 않다.
그 끔찍한 부분은 물론 원래의 포인터를 어디선가 저장해야 자유()를 부를 수 있다는 것이다.그래서 전반적으로 나는 이 디자인의 지혜가 정말 의심스럽다.
약 16바이트를 추가한 다음 포인터 아래에 (16-mod)를 추가하여 정렬된 16비트로 원래의 ptr을 푸시할 수도 있다.
main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );
printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );
free(mem1);
}
단 하나의 바이트를 낭비할 수 없는 제약조건이 있는 경우, 이 솔루션은 작동한다: 참고:이것을 무한히 실행할 수 있는 경우가 있다 :D
void *mem;
void *ptr;
try:
mem = malloc(1024);
if (mem % 16 != 0) {
free(mem);
goto try;
}
ptr = mem;
memset_16aligned(ptr, 0, 1024);
솔루션을 위해 나는 메모리를 정렬하고 단일 바이트의 메모리를 낭비하지 않는 패딩의 개념을 사용했다.
제약조건이 있다면 단 하나의 바이트도 낭비할 수 없다.말록과 함께 할당된 모든 포인터는 16바이트 정렬이다.
C11이 지원되므로 그냥 전화하면 된다.aligned_alloc (16, size)
.
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
'Programing' 카테고리의 다른 글
Vuex 돌연변이 반환이 마지막으로 삽입됨 (0) | 2022.04.15 |
---|---|
선택 입력 /w Vuex 저장소 바인딩에 의한 동적 구성 요소 (0) | 2022.04.14 |
Vue 선택, v-for 및 v-model이 포함된 값 사전 선택 (0) | 2022.04.14 |
문자 배열 선언에서 문자열 리터럴 주위에 있는 브레이스가 유효한가?(예: char s[] = {"Hello World"}) (0) | 2022.04.14 |
Vue+webpack+vue-loader 프로젝트의 다른 js 파일에서 기능을 가져오는 방법 (0) | 2022.04.14 |