2017-12-05 38 views
0

최근 RE2 라이브러리가 빠른 세트 조회를 위해 this technique을 사용한다는 사실을 발견했습니다. 조회 중에는 초기화되지 않은 배열의 값이 사용됩니다.이 값은 정의 된대로 동작합니다.잘 정의 된 것으로 표시된 RE2의 정의되지 않은 동작

심지어 초기화되지 않은 메모리 사용에 대한 경고가있는 this issue을 발견했습니다. 그러나 문제는이 행동이 간판된다는 의견으로 끝났다.

실제로 초기화되지 않은 배열은 모든 최신 컴파일러와 아키텍처에 임의의 데이터가 포함되어 있다고 가정합니다. 그러나 다른 한편으로는 '정의되지 않은 동작'문을 '문자 그대로 일어날 수있는 일'(하드 드라이브 나 고질라가 여러분의 프로그램 형식을 포함하여 도시를 파괴 함)을 처리합니다.

질문 : C++에서 초기화되지 않은 데이터를 사용할 수 있습니까?

+0

초기화되지 않은 데이터의 내용은 * indeterminate *이며 데이터 형식에 따라 [트랩 표현 *] (https://stackoverflow.com/questions/6725809/trap-representation)을 포함 할 수 있습니다. C++에서 초기화되지 않은 메모리에 대한 모든 읽기는 [정의되지 않은 동작]입니다 (http://en.cppreference.com/w/cpp/language/ub). C++과 C 사이에는 다른 점이 있습니다. C에서는 UB 만 있기 때문입니다. 값이 트랩 표현이면 그렇지 않음). –

+0

기본적으로 _ "정의되지 않은 동작을 호출하도록 정의되어 있습니까?"_ –

+0

@underscore_d : 표준은 컴파일러가 광범위하게 설명 된 * 모든 * 동작을 정의 할 일반적인 의무가 없음을 의미하는 문과 (2) 컴파일러는 광범위하게 정의 된 클래스 내에서 * 모든 * 동작의 동작을 제한 할 의무가 없습니다. 특정 동작도 의미가 다른 곳에서 정의됩니다. 일반적인 행동의 행동이 정의되지 않는다는 동일한 표현은 두 상황 모두에서 사용된다. 표준이 명시 적으로 설명을 충족하는 모든 동작의 동작을 정의하는 것을 거부한다는 사실은 ... – supercat

답변

0

C는 원래 설계되었을 때, arr 경우 TN 바이트를 점유하는 몇 가지 유형의 배열, arr의 기본 주소를 가지고 "의미 arr[i] 같은 표현, 그것에 i*N을 추가 결과 주소 N 바이트를 가져이었다 그들을 T로 해석하십시오. " N 바이트의 가능한 모든 조합이 유형 T으로 해석 될 때 의미가있는 경우 초기화되지 않은 배열 요소를 가져 오는 것은 임의의 값을 가져올 수 있지만 그렇지 않은 경우 예측할 수 있습니다. T이 32 비트 유형이면 T 유형의 초기화되지 않은 배열 요소를 읽으 려하면 최대 4294967296 개의 가능한 동작 중 하나가 발생합니다. 4294967296 행동이 프로그램의 요구 사항을 충족시키는 경우에만 그러한 행동이 안전합니다. 참고로, 그러한 보증이 유용한 상황이 있습니다.

그러나 C 표준은 초기화되지 않은 배열 요소를 읽으려는 시도가 저장소에있을 수있는 비트 패턴과 일치하는 방식으로 작동하지 않는다는 의미 상 약한 언어를 설명합니다. 컴파일러 작성자는 Dennis Ritchie가 발명 한 언어보다는이 약한 언어를 처리하기를 원합니다. 왜냐하면 상호 작용 방식에 관계없이 여러 가지 최적화를 적용 할 수 있기 때문입니다. 예를 들어, 코드가 a=x;을 수행하고 나중에 b=a;c=a;을 수행하고 컴파일러가 a 또는 x을 변경할 수있는 원래 할당과 이후의 것 사이에 아무 것도 볼 수 없으면 첫 번째 할당을 생략하고 후자를 변경할 수 있습니다 b=x;c=x;에 대한 두 개의 지정. 그러나 후자의 두 과제간에 어떤 일이 발생하여 x이 변경되면 bc이 다른 값을 가지게 될 수 있습니다. 아무 것도 변경하지 않으면 무언가가 변경됩니다. a.

아무 것도 변경하지 않으면 최적화 자체를 적용해도 문제가되지 않습니다. x하면 안됩니다. 반면에 할당 된 저장소를 유형 float으로 사용하는 코드를 해제하고 다시 할당 한 다음 유형을 int으로 사용하는 코드를 고려하십시오. 컴파일러가 원본 및 대체 요청의 크기가 동일하다는 것을 알고 있으면 저장소를 해제하고 다시 할당하지 않고 저장소를 재활용 할 수 있습니다.성능은 쓰기의 속도가 느린 계산의 결과를 처리에서 특히 이익을 위해

float *fp = malloc(4); 
... 
startSlowCalculation(); // Use some pipelined computation unit 
int *ip = (int*)fp; 
... 
b=*ip; 

*fp = resultOfSlowCalculation(); // ** Moved from up above 
somethingElse = *fp; 

... 
c=*ip; 

그것은 드문 것 :

float *fp = malloc(4); 
... 
*fp = slowCalculation(); 
somethingElse = *fp; 
free(fp); 
int *ip = malloc(4); 
... 
a=*ip; 
b=a; 
... 
c=a; 

은 다음과 같이 다시 작성하려면 : 즉, 그러나, 코드 시퀀스가 ​​발생할 수 있습니다 bc. 불행히도, 컴파일러는 지연 계산이 우연히 문제를 일으킬 수있는 지점에 우연히 도착하지 않을 것이라고 보장하는 것이 편리하도록 설계되지 않았습니다.

개인적으로 필자는 컴파일러 작성자의 철학을 잘못 판단한 것으로 간주한다. 특정 상황에있는 프로그래머가 보증이 유용하다는 것을 알고 있다면, 프로그래머가 그 부족을 해결하기 위해 100 % 확실성으로 상당한 비용을 부과 할 것을 요구한다. 대조적으로, 컴파일러가 보증의 부족을 전제로하는 최적화를 자제하는 요구 사항은 거의 들지 않습니다 (결핍을 해결하기위한 코드는 어쨌든 "최적화"를 거의 확실하게 차단할 것입니다). 불행히도, 표준을 위주로하는 것 이상으로 보장 할 필요가없는 소스 텍스트의 성능을 컴파일러가 유용한 태스크를 수행하기위한 코드를 생성 할 수있는 최적화하는 것보다 성능을 최적화하는 데 관심이있는 사람들이 있습니다.