2017-01-26 4 views
3

내가 할당 된 메모리에 할 수있는 일에 제한이 있습니까 원하는 것을 할 수있다 (현명한 표준)은 내가 할당 된 메모리와

예를 들어

#include <stdio.h> 
#include <stdlib.h> 

struct str{ 
    long long a; 
    long b; 
}; 

int main(void) 
{ 
    long *x = calloc(4,sizeof(long)); 
    x[0] = 2; 
    x[3] = 7; 
//is anything beyond here legal(if you would exclude possible illegal operations) 
    long long *y = x; 
    printf("%lld\n",y[0]); 
    y[0] = 2; 
    memset (x,0,16); 
    struct str *bar = x; 
    bar->b = 4; 
    printf("%lld\n",bar->a); 
    return 0; 
} 

가 요약하면 :

  • 크기가 맞는 한 다른 데이터 유형과 구조체에 대한 포인터를 다시 작성할 수 있습니까?
  • 작성하기 전에 읽을 수 있습니까?
  • 만약 내가 쓴 후에 읽을 수 없습니까?
  • 할당 된 메모리보다 작은 구조체와 함께 사용할 수 있습니까?

답변

4

y[0]에서부터의 읽기는 엄격한 앨리어싱 규칙을 위반합니다. 유효한 값 유형 long의 객체를 읽기 위해 유형이 long long 인 lvalue를 사용합니다.

해당 행을 생략한다고 가정하십시오. 다음 성가신 부분은 memset(x,0,16);입니다. This answermemset이 유효 유형을 업데이트하지 않는다고 주장합니다. 표준이 명확하지 않습니다.

memset이 유효 유형을 변경하지 않는다고 가정하면; 다음 호는 bar->a입니다.

C 표준은 이에 대해서도 명확하지 않습니다.어떤 사람들은 bar->a(*bar).a을 의미한다고 말합니다. 우리는 bar 개체를 먼저 위치에 쓰지 않았기 때문에 엄격한 별칭 위반입니다.

다른 사람 (나를 포함하여)은 괜찮다고 말합니다. 액세스에 사용 된 유일한 lvalue는 bar->a입니다. 즉, long long 유형의 lvalue이고 유효한 유형 long long (y[0] = 2;으로 작성된 객체)에 액세스합니다.

이러한 문제를 명확히하기 위해 엄격한 별칭 지정을 개선하기 위해 노력하는 C2X 작업 그룹이 있습니다.

+0

[6.5p6] (http://port70.net/~nsz/c/c11/n1570.html#6.5p6)은'memove'와 회사가 유효 유형을 수정한다는 것을 의미합니다. – StoryTeller

+0

@StoryTeller memmove와 memcpy는 확실히 그렇지만 memset이 같은 우산 아래 있다고 생각하지 않습니다. 만약 memset이 유효 타입을 설정했다면 (char - 그 외 무엇인가?), 일반적인 inti (memset (x, 0, n);)는 int를 초기화하여 UB로 유도 할 것이므로, 그렇게하지 않을 것이라고 생각합니다. 실용적인 해석을해라. –

+0

'bar-> b'에 대한 읽기 또는 쓰기는 같은 별칭이 될 것이므로 괜찮을 까? –

3

크기가 맞는 한 포인터를 다른 데이터 유형으로 다시 채울 수 있습니까?

은 할당 한 메모리 크기보다 큰 데이터 유형으로 다시 작성할 수 있습니다. 그러나 모든 코팅 된 물체의 유효 유형을 변경하려면 값을 써야합니다. 6.5p6

작성하기 전에 읽을 수 있습니까?
내가 작성한 후에 읽을 수 없습니까? 달리 언급 할 때를 제외

제 2 (calloc는 달리 임) 메모리의 값은 불확정이다. 트랩 값을 포함 할 수 있습니다. 또 다른 유형은 UB와 같이 값을 재 해석하기 위해 캐스트, 엄격한 앨리어싱 (6.5p7)

내가 할당 된 메모리보다 작은 구조체로 사용할 수 있습니다 위반?

네,하지만 그건 낭비입니다.


1 먼저 void*으로 캐스팅해야합니다. 그렇지 않으면 호환되지 않는 포인터 유형에 대한 컴파일러의 정당한 불만을 얻게됩니다.
심지어 일부 유형은 완전히 0 비트 패턴을 트랩 할 수 있으므로 의존합니다.

+0

첫 번째 대답으로 나머지는이 작업에 종속되어 있기 때문에 나머지 작업이 완료되었습니다. –

+1

@ 카미 카즈 - 내 녹슨 표준에 대한 M.M의 의견을 반영하여 편집 됨. – StoryTeller

0

대부분의 컴파일러는 관련된 데이터 유형에 관계없이 포인터의 읽기 및 쓰기가 수행되는 순서대로 기본 저장소에서 작동하는 모드를 제공합니다. 이 표준은 컴파일러가 그런 모드를 제공 할 것을 요구하지 않지만, 모든 질적 인 컴파일러가 그렇게 할 수 있다고 말할 수있는 범위까지 말합니다. 예에서 것을

float f; 
float test(int *p) 
{ 
    f=1.0f; 
    *p = 2; 
    return f; 
} 

참고 : 다음과 같은 코드를 부여 할 때

는 게시 된 근거에 의하면, 표준의 저자는 비관적 앨리어싱 가정을 만들기 위해 컴파일러를 피할 명시된 목적과 언어에 앨리어싱 제한을 추가 비록 위의 내용과 비슷하지만, p 포인터를 통해 f이 사용하는 저장소를 수정하는 것이 합법적이라고하더라도 코드를 보는 합당한 사람은 그러한 일이 일어날 가능성이 있다고 생각할 이유가 없습니다 .

float f; 
float test(float *p) 
{ 
    f=1.0f; 
    *(int*)p = 2; 
    return f; 
} 

하나는 코드가 float에 의해 사용되는 스토리지를 수정하지 않을 것이라고 생각하는 의도적으로 둔각 될 것이다, 그 결과가 있었다 : 반면에, 많은 컴파일러 작가는 같은 주어진다면 것으로 인식 질 좋은 컴파일러가 *(int*)p에 대한 쓰기를 float에 대한 잠재적 쓰기로 간주해서는 안되는 이유는 없습니다.

아쉽게도 컴파일러 작성자는 유형 기반 앨리어싱 "최적화"로 인해 때로는 표준이 허용하는 것 이상으로 명확하고 명백하게 갈수록 공격적으로 변했습니다. 프로그램이 다른 시간대에 다른 유형의 저장소에 액세스 할 필요가 없다면 지원하는 컴파일러에서 -fno-strict-aliasing 옵션을 사용하는 것이 좋습니다. 그렇지 않으면 표준을 준수하고 현재 작동하지만, "최적화"로 더욱 공격적으로 된 컴파일러의 향후 버전에서는 실패 할 수 있습니다.

PS - 유형 기반 앨리어싱을 사용하지 않도록 설정하면 상황에 따라 코드 성능에 영향을 줄 수 있지만 restrict- 수정 된 변수 및 매개 변수를 올바르게 사용하면 비관적 앨리어싱 가정 비용이 발생하지 않습니다. 조금주의를 기울이면 이러한 한정어를 사용하면 공격적 앨리어싱이 수행 할 수있는 것과 동일한 최적화가 가능하지만 더 안전하게 수행 할 수 있습니다.

+0

답변을 완전히 이해하려면 약간의 독서를해야 할 것 같습니다. 자세한 설명 주셔서 감사합니다. –

+0

@ 카미 카제 :'restrict'에 대한 읽기; 유형 기반 앨리어싱을 비활성화하지 않아도 성능상의 이점을 제공 할 수 있지만 그렇게하는 경우 특히 중요해질 수 있습니다. – supercat