2009-02-03 4 views
21

대부분의 숙련 된 프로그래머는 데이터 정렬이 프로그램의 성능에 중요하다는 것을 알고 있습니다. 필자는 프로그래머가 필요한 것보다 더 큰 버퍼 크기를 할당하는 프로그램을 작성하고 정렬 된 포인터를 시작으로 사용하는 것을 보았습니다. 나는 내 프로그램에서 그렇게해야하는지 궁금하다. C++의 새로운 연산에 의해 반환 된 주소의 정렬을 보장하지 못한다. 그래서 테스트 할 수있는 작은 프로그램을 작성C++의 새 연산에 의한 주소 반환 정렬을 보증합니까?

for(size_t i = 0; i < 100; ++i) { 
    char *p = new char[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    short *p = new short[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    float *p = new float[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
system("pause"); 

내가 사용하고 컴파일러 모든 주소가 반환 된 새로운 작업이 정렬 된 것으로 보인다 비주얼 C++ 익스프레스 2008 년이다. 그러나 나는 확실하지 않다. 그래서 내 질문은 : 어떤 보증이 있습니까? 그들이 보증을한다면, 나는 자신을 조정할 필요가 없다. 그렇지 않다면,해야만한다.

답변

19

정렬이 표준에서 다음 보증 (3.7.3.1/2)를 갖는다 : 그것은 모든 완전한 개체 유형의 포인터로 변환 할 수 있도록

리턴 된 포인터가 적절하게 정렬되어야하고 그런 다음 할당 된 저장소에있는 객체 또는 배열에 액세스하는 데 사용됩니다 ( 까지 저장 장치가 해당 할당 해제 함수를 호출하여 명시 적으로 할당 취소됩니다).

편집 : 보증이 보유하지 않는 bug GCC의 /의 glibc를 강조 표시 timday 감사합니다.

EDIT 2 : Ben의 의견은 얽힘의 경우를 강조합니다. 할당 루틴에 대한 요구 사항은 표준에 의해서만 제공됩니다. 응용 프로그램에 자체 버전이 있으면 결과에 대한 보증이 없습니다.

+0

이론적으로. 실제로 32 비트 시스템에서 gcc + glibc 및 SSE 유형을 사용하는 경우 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795에 유의하십시오. – timday

+0

@timday : 최근 버전의 MSVC++ (예 : .NET 2003)에서도 SSE 유형의이 문제가 발생했습니다. 최신 버전으로 테스트하지 않았지만 여전히 그렇다고 생각됩니다. –

+0

'new []()'도우미 할당 함수에서 반환 된 값은'new [] '연산자에서 얻은 포인터와 같지 않습니다. 이 대답은 잘못되었습니다. –

4

플랫폼의 새로운/new [] 연산자는 기본 데이터 유형 (double, float 등)에서 잘 수행되도록 충분한 정렬로 포인터를 반환합니다. 적어도 현명한 C++ 컴파일러 + 런타임은 그렇게해야합니다.

SSE와 같은 특수 정렬 요구 사항이있는 경우 특별한 aligned_malloc 함수를 사용하거나 직접 롤오버하는 것이 좋습니다.

4

나는 자신이 사용하기 위해 홀수 비트를 확보하기 위해 정렬을 사용하는 시스템에서 작업했습니다!

그들은 홀수 비트를 사용하여 가상 메모리 시스템을 구현했습니다.

포인터에 홀수 비트 세트가있는 경우 데이터에 데이터가 아닌 데이터를 가져 오기위한 정보에 해당 포인터가 가리키는 (홀수 인 비트) 것을 나타 내기 위해 사용되었습니다.

나는 이것이 자신의 이익을 위해 아주 영리한 코딩의 더러운 비트라고 생각했다 !!

토니

+2

태그가 지정된 포인터라고하며 전혀 드문 것이 아닙니다. 많은 프로그래밍 언어 구현은이 트릭을 사용하여 포인터와 정수를 구별합니다. – geocar

+1

그리고 ARM 인터 워킹은이를 사용합니다 - 적용 가능한 경우 ARM 모드 코드 주소가 짝수이고 엄지 모드 주소가 홀수입니다. 노드의 하위 트리의 높이 차이를 저장하기 위해 맨 아래 두 비트를 사용하는 AVL 트리 구현을 보았습니다.제한된 시스템에서는 깃발 비트 수 :-) –

+0

MAC OS (클래식)의 초기 버전에서는 메모리 관리자로 상위 8 비트를 사용했습니다. 68000의 ptrs는 최대 24 비트 만, 주소는 32 비트를 등록합니다. – AnthonyLambert

7

덧붙여는 MS documentation 정렬 16 바이트입니다 malloc에 ​​/ 새 반환 주소에 대해 뭔가를 언급하지만, 실험에서이 사건을하지 않습니다. 필자는 프로젝트를 위해 16 바이트 정렬이 필요했는데 (결국 확장 된 명령어 세트로 메모리 복사 속도를 높이기 위해), 결국에는 할당자를 작성하는 데 의지했다.-

+0

나는 너의 고통을 느낀다 ... –

9

이 늦은 대답하지만, 단지 리눅스에 대한 상황을 명확히하기 위해 64 비트 시스템에 메모리는 항상 16 바이트로 정렬입니다 :

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

에 의해 반환 된 블록의 주소 GNU 시스템에서 malloc 또는 realloc은 항상 의 8 배수 (또는 64 비트 시스템에서는 16)입니다.

new 연산자 malloc 내부적 그래서뿐만 아니라 new 적용 (./gcc/libstdc++-v3/libsupc++/new_op.cc 참조)를 호출한다.

glibcmalloc의 일부 구현은 기본적 MALLOC_ALIGNMENT2*sizeof(size_t)size_t = 4 바이트는 32 비트 및 각각 x86-32 및 - 64 시스템에서, 64 비트 = 8byte 인 것으로 정의한다.

$ cat ./glibc-2.14/malloc/malloc.c: 
... 
#ifndef INTERNAL_SIZE_T 
#define INTERNAL_SIZE_T size_t 
#endif 
... 
#define SIZE_SZ    (sizeof(INTERNAL_SIZE_T)) 
... 
#ifndef MALLOC_ALIGNMENT 
#define MALLOC_ALIGNMENT  (2 * SIZE_SZ) 
#endif 
관련 문제