2016-07-13 3 views
15

질문 :이 코드가 엄격한 앨리어싱 규칙을 위반합니까?

  1. 다음이 코드는 엄격한 앨리어싱 규칙을 위반 하는가? 즉, 똑똑한 컴파일러는 int*을 통해 다른 유형으로 처음 액세스 한 버퍼에 액세스 할 수 있기 때문에 00000 (또는 다른 불쾌한 효과)을 인쇄 할 수 있습니까? 그렇지 않으면 그것을 깰 (ptr1이 범위에 관해서는, 그래서 ptr2가 이미 정의 될 것이다)

  2. 는 괄호 앞에 바로 정의와 ptr2의 initializaton 움직이는 것?

  3. 그렇지 않은 경우 중괄호를 제거 할 것입니다 (따라서 ptr1ptr2는 같은 범위에있었습니다).

  4. 예인 경우 어떻게 코드를 수정할 수 있습니까?

보너스 질문 : 코드를 확인하고, 2 또는 3 중 하나를 중단하지 않으면 엄격한 앨리어싱 규칙 끊을 수 있도록 변경하는 방법 (예를 들어, 브레이스 루프 int16_t를 사용하는 변환) ?


int i; 
void *buf = calloc(5, sizeof(int)); // buf initialized to 0 

{ 
    char *ptr1 = buf;  
    for(i = 0; i < 5*sizeof(int); ++i) 
     ptr1[i] = i; 
} 

int *ptr2 = buf; 
for(i = 0; i < 5; ++i) 
    printf("%d", ptr2[i]); 

확인을 찾고

, 너무 짧은 (틱) 이상적으로 최소한의 표준 따옴표이 특정 코드에 대한 전문가의 대답은, 내가 후 나는 것입니다. 필자는 엄격한 앨리어싱 규칙에 대해 오랫동안 설명하지 않고이 코드와 관련된 부분 만 설명합니다. 그리고 대답이 명시 적으로 위의 번호가 매겨진 질문을 열거한다면 그것은 좋을 것입니다.

정수 트랩 값이없는 범용 CPU라고 가정하고 int이 32 비트 및 2의 보수라고 가정 해 봅시다.

답변

13

아니요,하지만 이것은 메모리가 할당되어 문자 유형을 사용하여 기록되기 때문입니다.

메모리는 malloc을 사용하여 할당됩니다. 이 객체는 malloc으로 할당 되었기 때문에 으로 선언하지 않았습니다. 따라서 객체에는 효과적인 유형이 없습니다.

그런 다음 코드는 char 유형을 사용하여 개체에 액세스하고 수정합니다. 타입 2char하고 효율적인 형태를 갖는 어떤 개체 5가 복사이 이후의 액세스를위한 char에 효과적인 형태를 설정하지 않고 복사되지 않고, 단 기간 동안, char에 효과적인 유형을 설정으로서 액세스 . 액세스 후에는 오브젝트에 더 이상 효과적인 유형이 없습니다.

그런 다음 유형이 int 인 경우 해당 객체에 액세스하고 읽기 전용으로 사용됩니다. 객체가 유효한 유형이 아니기 때문에, 읽는 동안 객체는 int이됩니다. 액세스 후에는 객체에 더 이상 유효한 유형이 없습니다. int이 분명히 유효한 유형 int과 호환 되었기 때문에 동작이 정의됩니다.

(값이 int 트랩 표현하지 읽기 가정.)

하는

당신이 액세스하고도 int와 호환되지 않는 문자 형식을 사용하여 객체를 수정 더라면, 행동이 정의되지 않은 것 .

의 당신의 예 (sizeof(float)==sizeof(int) 가정) 한 가정 해 봅시다 :

int i; 
void *buf = calloc(5, sizeof(float)); // buf initialized to 0 

{ 
    float *ptr1 = buf;  
    for(i = 0; i < 5*sizeof(float); ++i) 
     ptr1[i] = (float)i; 
} 

int *ptr2 = buf; 
for(i = 0; i < 5; ++i) 
    printf("%d", ptr2[i]); 

float의가에 기록되고있는 객체의 유효 유형, 쓰기의 기간과 이후의 모든 들어, float의된다 객체를 수정하지 않는 객체에 액세스 . 그런 다음 해당 객체가 int에 의해 액세스 될 때 값은 읽기 전용으로 변경되기 때문에 유효 유형은 float으로 유지됩니다. float을 사용한 이전의 쓰기는이 객체에 대한 다음 쓰기 (이 경우에는 발생하지 않음) 때까지 유효 유형을 float으로 영구적으로 설정합니다. intfloat 유형은 과 호환되지 않으므로 동작이 정의되지 않습니다.


을 (: ISO 아래 모든 텍스트에서 인용 IEC 9899 : 201x)

1은 (6.5 식 6)
그 저장된 값에 액세스하기위한 오브젝트의 유효 유형 인 객체의 선언 된 형태 (존재하는 경우) 87) 할당 된 객체에는 선언 된 유형이 없습니다.

2

(6.5 식 6)
이 값은 캐릭터 타입없는 타입 갖는 좌변을 통해 더 선언 유형을 갖지 않는 개체에 저장되어있는 경우, 그 좌변의 유형은 실효된다 해당 액세스에 대한 오브젝트 유형 및 저장된 값을 수정하지 않는 후속 액세스에 대한 오브젝트 유형.

3

(6.5 식 6)이 다른 어떤 유형 선언이없는 개체에 대한 액세스를 위해
객체의 유효 입력은 단순히 접속을 위해 사용되는 좌변의 유형이다. 객체의 유효 유형과 호환 형 - 88) :

4

(6.5 식 8)
목적은 저장된 값 하나 다음 유형 갖는다 좌변 식에서만 액세스 가진다 , - 객체의 유효 유형과 호환되는 유형의 정규화 된 버전 - 객체의 유효 유형에 해당하는 부호가 있거나 부호없는 유형 인 유형 - 유형이 부호가 있거나 부호가없는 유형 유효한 유형의 유효 형식에 해당하는

(6.5 표현식 6)
값이 memcpy 또는 memmove를 사용하여 선언 된 유형이없는 객체로 복사되거나 문자 유형의 배열로 복사 된 경우 해당 액세스 및 이후 액세스에 대해 수정 된 객체의 유효 유형 값을 수정하지 않음 은 값이 복사되는 객체의 유효한 유형입니다 (있는 경우).

+0

내가 생각하지 못한 흥미로운 점을 암시 적으로 제기한다 : 할당 된 메모리가'int' 값을 통해 할당되지 않았기 때문에, 6.5 절 6 절에'int'가되지 않을까? 'int * '를 통해 참조 해제되는데, 결국 엄격한 앨리어싱을 위반하게됩니까? 그 단락을 분석하는 것은 고통 스럽다. –

+1

그 시점에서 문자에만 개체에 기록 된 및 그 개체에 유효한 형식을 설정하지 않았습니다. 선언 된 타입이없는 객체에 대한 다른 모든 접근의 경우, 객체의 유효 타입은 단순히 접근에 사용 된 lvalue의 타입 일뿐입니다 * – 2501

+1

같은 포인트에서'int '가 읽기 대신 객체에 쓰여지는 경우 다음과 같은 이유로 유형이 'int'가됩니다. * 값이 문자 유형이 아닌 유형을 가진 lvalue를 통해 선언 된 유형이없는 객체에 저장되는 경우 lvalue의 타입은 해당 액세스와 저장된 값을 수정하지 않는 후속 액세스에 대한 객체의 효과적인 유형이됩니다. * – 2501

2

아니요. 엄격한 앨리어싱을 위반하지 않습니다. the C Standard에서

, 6.2.5 유형, 단락 28 :

void에 대한 포인터가 같은 표현과 문자 형식에 대한 포인터로 정렬 요구 사항을 가진다. 48

참고 지칭 48 48 각주 :

48)와 동일한 표시 및 정렬 요구가 인수 함수로서 상호 교환 가능성을 암시 을 의미하는, 리턴 값 함수 및 구성원 노조의

그래서 당신이 char * 포인터를 통해 calloc() 'D 메모리에 액세스 할 수없는 문제 (당신의 ptrptr1로 의미 가정).

이 포인터가 할당 적절히 그래서 이 포인터에 할당 될 수 있음을 정렬 성공하면 반환 7.22.3 메모리 관리 기능, 제 1 주부터

이 정말 추가 비록, 다음으로 인하여 A 기본 정렬 요구와 객체의 유형과는 같은 개체 또는 그래서 당신은

를 할당 된 공간에서 같은 객체의 배열에 액세스하는 데 사용 포인터 char뿐만 아니라 int 포인터를 통해 calloc() '메모리에 안전하게 액세스 할 수 있습니다. 그리고 double 부팅 포인터 (할당 된 메모리의 경계 내에 있다고 가정).

+6

엄격한 앨리어싱은 정렬 요구 사항 및 표현과 다른 것입니다. 문제는 언급 무엇 JohannesSchaub - litb @ –

+1

같은 일'memset 함수()'않습니다 (* 의 값이' 을 c' 'memset' 기능 사본 (AN 로 변환입니다'부호 char' ) 각 객체의 첫 번째 의 'n' 문자가 가리키는 객체의 문자가 's' . *) 잘못되었다고 생각하면 'memset()'이 메모리를 반복 된'char' 값으로 설정하십시오. 'memset()'이 엄격한 앨리어싱을 위반한다고 말하고 있습니까? –

+3

@AndrewHenle 질문에있는 코드가 엄격한 앨리어싱 규칙에 위배된다는 것은 아무도 없습니다. 그러나 이것이 답변에 제공된 이유에 대한 이유는 정확하지 않습니다. 동일한 정렬과 표현을하는 것은 두 가지 유형이 별칭이 될 수 있음을 의미하지 않습니다. – davmac

관련 문제