3

series of articles에서 Dan Saks는 C에서 가능한 가상 함수 구현을 소개합니다. 정적 유형 확인에 더 의존하기 때문에 이것은 the solution of A.-T. Schreinervoid * 포인터 및 동적 유형 확인과 반대되는 다른 접근 방법입니다. 여기 C에서 가상 상속의 예가 정의되지 않은 동작을 이용하고 있습니까?

vptr S 삭스 '버전 vtable의 박리없이 다운 일례이다 (편의상, 함수 포인터 struct Base struct Derived 및 단 부재이다).

요점은 함수 호출 (및 캐스트) Base_get_param((Base *) d)입니다. 이것은 함수 포인터 int (*get_param)(Derived const *self)int (*get_param)(Base const *self)으로 "암시 적으로 캐스트"된다는 것을 의미합니까? 호환되지 않는 유형 때문에 여기 (C99 및 C11 표준에 따라) 정의되지 않은 동작을 악용합니까?

나는 GCC 4.8과 clang 3.4 모두에서 적절한 출력을 얻는다. 위의 구현이 손상 될 수있는 상황이 있습니까?

기능 포인터 캐스트 및 호환 유형에 대한 자세한 대답은 here입니다. 그러나이 경우에 대해서는 확실하지 않습니다. 예외가 있지만,

printf("%d\n", Base_get_param((Base *) d)); 
           ^^^^^^^^^ 

엄격한 앨리어싱 규칙이 다른 유형의 포인터를 통해 개체에 액세스하는 정의되지 않은 동작합니다

+0

이것은 정적 상속 vs 동적 상속 인 것으로 보입니다. 이것은 이전에 C에서 본 적이없는 것입니다. 그러나'Base * '를 기대하는 함수에'Derived *'를 전달할 수 없다는 것을 의미합니다. –

답변

1

이 프로그램은 실제로 undefined behavior를 호출 않습니다, 당신은 여기 strict aliasing rules의 위반이 char *에 대해 정의되지 않은 동작을 호출하지 않고 별칭을 지정할 수 있습니다.

기본적으로 컴파일러는 다른 유형의 포인터가 동일한 메모리를 가리 키지 않는다는 가정하에 최적화 할 수 있습니다. 정의되지 않은 동작을 호출하면 프로그램 결과가 예측할 수 없게됩니다.

실제적으로 이와 같은 경우 question 컴파일러에서 잘못된 작업을 수행하지 못했지만 다른 복잡한 경우에는 문제가 발생할 수 있습니다. 문제가 발생한 경우 gcc, strict-aliasing, and horror stories을 참조하십시오. 엄격한 앨리어싱을 위반 -O1-O2를 사용하여 서로 다른 출력을 생성

#include <stdio.h> 

void check (int *h, long *k) 
{ 
    *h = 5; 
    *k = 6; 
    printf("%d\n", *h); 
} 

int main (void) 
{ 
    long k; 
    check((int *)&k, &k); 
    return 0; 
} 

: 기사 Type Punning, Strict Aliasing, and Optimization 다음 코드를 제공합니다.

gcc에 대한 엄격한 앨리어싱은 -fno-strict-aliasing을 사용하여 해제 할 수 있으며 저자가이 기사에서 찾을 수는 없지만 그러한 가정을하고 있을지도 모릅니다. 이렇게하면 일부 최적화가 사용되지 않으므로 값이 비싼 플래그가 아닙니다.

+0

@ user2194731'-Wstrict-aliasing = 1'을 사용하여'gcc '라는 경고를 받는다. 나의 경험에 의하면 이런 종류의 위반에 대한 경고를받는 것에 의존해서는 안된다. –

+0

당신은 절대적으로 옳습니다! 나는 또한'-Wstrict-aliasing = 1'을 사용하여 경고를 얻으려고했지만 레벨 0이나 2는 사용하지 않았다. –