2009-05-08 2 views
84

나는 다음과 같은 코드가 있습니다스택 변수는 GCC __attribute __ ((aligned (x)))에 의해 정렬됩니까?

#include <stdio.h> 

int 
main(void) 
{ 
     float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0}; 
     printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]); 
} 

을 그리고 난 다음과 같은 출력이 있습니다

a[0]의 주소가 0x1000의 배수가 아닌 이유
0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac 

?

정확히 __attribute__((aligned(x)))은 무엇을합니까? 내가 오해 한 것 this 설명?

저는 gcc 4.1.2를 사용하고 있습니다.

답변

94

문제는 배열이 스택에 있다고 생각합니다. 함수가 시작될 때 스택 포인터가 아무 것도 될 수 없으므로 필요한 것보다 많이 할당하고 조정하지 않고 배열을 정렬 할 수있는 방법이 없습니다. 배열을 함수에서 전역 변수로 옮기면 작동 할 것입니다. 당신이 할 수있는 또 다른 일은 로컬 변수로 유지하는 것입니다 (아주 좋은 방법입니다). 그러나 그것을 static으로 만드십시오. 이렇게하면 스택에 저장되지 않습니다. 배열 복사본은 단 하나이므로이 두 가지 방법 모두 스레드로부터 안전하지 않거나 재귀 안전하지 않도록주의하십시오. 이 코드

:

#include <stdio.h> 

float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0}; 

int 
main(void) 
{ 
     printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]); 
} 

나는이 얻을 :

0x804c000 0x804c004 0x804c008 0x804c00c 

하는 예상되는 것입니다. 원래 코드를 사용하면 난 당신처럼 임의의 값을 얻을 수 있습니다.

+11

+1 정답. 또 다른 해결책은 로컬 어레이를 정적으로 만드는 것입니다. 스택의 정렬은 항상 문제이며이를 피하는 습관을 갖는 것이 가장 좋습니다. –

+0

오 예, 정적으로 생각하지 않았습니다. 이것은 이름 충돌을 방지하기 때문에 좋은 생각입니다. 나는 내 대답을 편집 할 것이다. – Zifre

+2

정적으로 만들면 재 호출이 불가능하고 스레드로부터 안전하지 않게됩니다. – ArchaeaSoftware

9

정렬은 모든 유형에 유효하지 않습니다. 당신은 읽어 줄 테니, 다음

#include <stdio.h> 

struct my_float { 
     float number; 
} __attribute__((aligned(0x1000))); 

struct my_float a[4] = { {1.0}, {2.0}, {3.0}, {4.0} }; 

int 
main(void) 
{ 
     printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]); 
} 

을 그리고 : 당신은 행동의 속성을 볼 수있는 구조를 사용하는 것이 좋습니다 당신이 기대했던 것입니다

0x603000 0x604000 0x605000 0x606000 

.

편집 : @yzap 및 다음 @Caleb 케이스 주석에 의해 밀려 초기 문제가 GCC 버전 때문이다. 나는 요청자의 소스 코드와 함께 GCC 4.4.1 대 GCC 3.4.6에 확인했습니다

$ ./test_orig-3.4.6 
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c 
$ ./test_orig-4.4.1 
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c 

그것은 나이가 GCC 버전 (어딘가에 4.4.1 이전) 정렬 병리를 보여줍니다 이니까 분명하다.

참고 1 : 제안 된 코드는 "배열의 각 필드 정렬"과 관련하여 질문에 대답하지 않습니다.

주 2 : main() 내부에서 정적이 아닌 A []를 가져 와서 GCC 3.4.6으로 컴파일하면 구조체 배열의 정렬 지시문이 깨지지 만 구조체간에 0x1000의 거리가 유지됩니다 ... 여전히 나빠요! (임시 해결책에 대한 답변은 @zifre를 참조하십시오)

+1

zifre가 답변 한 것처럼, 형식이 아니지만 사용자 버전에서 정적으로 설정 한 것입니다. – ysap

+0

@ysap, GCC 버전과 전역 적으로 작동하도록 만든 버전입니다. 의견을 주셔서 감사합니다! 나는 그것을 고치기위한 답을 편집했다. :) – levif

13

최근 GCC (4.5.2-8ubuntu4에서 테스트 됨)는 배열을 올바르게 정렬 한 상태에서 예상대로 작동하는 것처럼 보입니다.

#include <stdio.h> 

int main(void) 
{ 
    float a[4] = { 1.0, 2.0, 3.0, 4.0 }; 
    float b[4] __attribute__((aligned(0x1000))) = { 1.0, 2.0, 3.0, 4.0 }; 
    float c[4] __attribute__((aligned(0x10000))) = { 1.0, 2.0, 3.0, 4.0 }; 

    printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]); 
    printf("%p %p %p %p\n", &b[0], &b[1], &b[2], &b[3]); 
    printf("%p %p %p %p\n", &c[0], &c[1], &c[2], &c[3]); 
} 

내가 얻을 :

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc 
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c 
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c 
+0

배열이 스택에 할당되어 있다는 점을 감안하면 조금 놀랍습니다. 스택에 이제는 구멍이 가득 찼다는 것을 의미합니까? – ysap

+0

그의 스택은 16 바이트로 정렬됩니다. – user7116

38

스택 변수 작동하지 정렬 속성의 원인 GCC의 버그가 발생했습니다. 아래 링크 된 패치로 수정 된 것으로 보입니다. 아래의 링크에는 문제에 대한 토론도 포함되어 있습니다.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

나는 두 개의 서로 다른 GCC의 버전 위의 코드를 시도 : 레드햇 5.7 상자에서 4.1.2, 그리고가 0x1000에 정렬 어떠한 방식으로 WRE 로컬 배열 (문제와 유사 실패 바이트 경계). 그런 다음 RedHat 6.3에 gcc 4.4.6 코드를 사용해 보았습니다. 그리고 완벽하게 작동했습니다 (로컬 어레이가 정렬되었습니다). 나중에 버전에서 수정 될 것으로 보인다 GCC의 버그를 발견 것 같습니다, 어쨌든

http://code.mythtv.org/trac/ticket/6535

: 신화의 TV의 사람들은 (gcc가 패치는 위의 수정 듯) 비슷한 문제가 있었다.

+3

연결된 버그 gcc에 따르면 4.6은 모든 아키텍처에서 완전히 수정 된이 문제의 첫 번째 릴리스였습니다. – textshell