초기화되지 않은 지역 변수가 가장 빠른 난수 생성기입니까?


329

초기화되지 않은 로컬 변수가 정의되지 않은 동작 ( UB )이며 값에 트랩 표현이있을 수 있으므로 추가 작업에 영향을 줄 수 있지만 때로는 시각적 표현에만 임의의 숫자를 사용하고 다른 부분에서는 더 이상 사용하지 않으려는 경우가 있습니다. 예를 들어, 프로그램은 시각 효과에서 임의의 색상으로 무언가를 설정합니다.

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

그것보다 빠르니?

void updateEffect(){
    for(int i=0;i<1000;i++){
        star[i].setColor(rand()%255,rand()%255,rand()%255);
        star[i].setVisible(rand()%2==0?true:false);
    }
}

다른 난수 생성기보다 빠릅니다.


88
+1 이것은 완전히 합법적 인 질문입니다. 실제로 초기화되지 않은 값 임의적 일 수 있습니다. 그들이 특별히 아니고 그것이 UB라는 사실은 그렇게 나쁘게 요구 하지 않습니다 .
imallett

35
@ imallett : 물론입니다. 이것은 좋은 질문이며, 과거에 적어도 하나의 오래된 Z80 (Amstrad / ZX Spectrum) 게임 은 지형을 설정하기 위해 데이터로 프로그램 을 사용했습니다 . 따라서 선례도 있습니다. 요즘 그렇게 할 수 없습니다. 최신 운영 체제는 모든 재미를 없애줍니다.
Bathsheba

81
분명히 주요 문제는 무작위가 아니라는 것입니다.
john

30
실제로 초기화되지 않은 변수가 임의의 값으로 사용되는 예 는 데비안 RNG 재해 ( 이 기사의 예 4)를 참조하십시오 .
PaperBirdMaster

31
실제로 다양한 아키텍처에서 디버깅을 많이하고 있습니다. 솔루션은 초기화되지 않은 레지스터를 읽거나 초기화되지 않은 메모리를 읽는 두 가지 작업을 수행 할 수 있습니다. 이제 "초기화되지 않은"은 임의의 방식으로 임의를 의미하지만 실제로는 a) 0 , b) 반복 또는 일관된 값 (이전에는 디지털 미디어가 차지한 메모리를 읽는 경우) 또는 c) 제한된 값을 가진 일관된 가비지를 포함 가능성이 높습니다. 이전에 인코딩 된 디지털 데이터가 차지한 메모리를 읽는 경우 설정 합니다. 그것들 중 어느 것도 실제 엔트로피 소스가 아닙니다.
mg30rg

답변:


299

다른 사람들이 지적했듯이 이것은 UB (Undefined Behavior)입니다.

실제로, 그것은 아마도 (실제로) 작동 할 것입니다. x86 [-64] 아키텍쳐에서 초기화되지 않은 레지스터를 읽는 것은 실제로 가비지 결과를 낳을 것이며, 아마도 레지스터가 유효하지 않은 것으로 플래그 될 수있는 Itanium과는 달리 나쁜 일을하지 않을 것입니다 .

그래도 두 가지 주요 문제가 있습니다.

  1. 특히 무작위는 아닙니다. 이 경우 스택에서 읽고 있으므로 이전에 있던 것을 얻을 수 있습니다. 10 분 전에 입력 한 비밀번호 또는 할머니의 쿠키 레시피는 사실상 임의적이며 완전히 구조화 된 것일 수 있습니다.

  2. 이와 같은 것들을 코드에 포함시키는 것은 나쁜 일 (자본 'B') 입니다. 기술적으로, 컴파일러 reformat_hdd();는 정의되지 않은 변수를 읽을 때마다 삽입 할 수 있습니다 . 그것은 하지 않습니다 ,하지만 당신은 어쨌든 그것을 할 것이다. 안전하지 않은 일을하지 마십시오. 당신이 만드는 적을 예외는 안전 당신은 우발적 인 실수 있습니다 모든 시간.

UB의 더 시급한 문제는 전체 프로그램의 동작이 정의되지 않는다는 것입니다. 최신 컴파일러는이 기능을 사용하여 엄청난 양의 코드를 제거하거나 시간을 거슬러 올라갈 수 있습니다 . UB와 함께 노는 것은 살아있는 원자로를 해체하는 빅토리아 엔지니어와 같습니다. 심각한 문제가 발생할 수 있으며 기본 원칙 또는 구현 된 기술의 절반을 모를 것입니다. 그것은 수도 괜찮을,하지만 당신은 여전히이 일어나게해서는 안된다. 자세한 내용은 다른 멋진 답변을보십시오.

또한, 나는 당신을 해고합니다.


39
@Potatoswatter : Itanium 레지스터는 사실상 "초기화되지 않은 레지스터"인 NaT (Not Thing)를 포함 할 수 있습니다. Itanium에서는 등록하지 않은 레지스터에서 읽으면 프로그램이 중단 될 수 있습니다 (자세한 내용은 다음을 참조하십시오 : blogs.msdn.com/b/oldnewthing/archive/2004/01/19/60162.aspx ). 따라서 초기화되지 않은 값을 읽는 것이 정의되지 않은 동작 인 이유는 충분합니다. Itanium이 그다지 인기가없는 이유 중 하나 일 것입니다. :
tbleher

58
나는 "일하는 작품"개념에 반대한다. 그것이 사실이 아닌 오늘날에도 사실이더라도, 더 적극적인 컴파일러로 인해 언제든지 변경 될 수 있습니다. 컴파일러는 읽은 내용을 대체하고 unreachable()프로그램의 절반을 삭제할 수 있습니다. 이것은 실제로도 발생합니다. 이 행동은 내가 믿는 일부 Linux 배포판에서 RNG를 완전히 중화시켰다.; 이 질문에 대한 대부분의 답변은 초기화되지 않은 값이 전혀 값처럼 동작한다고 가정하는 것 같습니다. 그건 틀렸어
usr

25
또한 모범 사례를 가정하면 코드 검토에서 논의되고 논의되고 다시는 발생하지 않아야한다고 가정하면 다소 어리석은 말처럼 보일 것입니다. 우리가 올바른 경고 플래그를 사용하고 있기 때문에 이것은 분명히 포착되어야합니다.
Shafik Yaghmour

17
@Michael 사실입니다. 프로그램이 언제든지 정의되지 않은 동작을하는 경우 컴파일러는 정의되지 않은 동작을 호출하기 전에 코드에 영향을주는 방식으로 프로그램을 최적화 할 수 있습니다 . 이것이 마음에 들지 않는 방법에 대한 다양한 기사와 데모가 있습니다. blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx (표준에 언급 된 비트 포함) 프로그램의 경로가 UB를 호출하면 모든 베팅이 해제됩니다)
Tom Tanner

19
이 대답은 마치 "정의되지 않은 행동을 불러오는 것이 나쁜 것은 아니지만 실제로 실제로 당신을 크게 해치지 않을 것" 처럼 들린다 . 그건 틀렸어요. UB가 발생할 것이라고 표현에서 엔트로피를 수집 할 수있는 (그리고 아마도 것이다 원인) 모든 이전에 수집 된 엔트로피가 손실 될 수 . 이것은 심각한 위험입니다.
Theodoros Chatzigiannakis

213

이를 명확하게 말하겠습니다 : 프로그램에서 정의되지 않은 동작을 호출하지 않습니다 . 결코 좋은 생각, 기간이 아닙니다. 이 규칙에는 예외가 있습니다. 예를 들어, offsetof 구현 하는 라이브러리 구현자인 경우 . 귀하의 사례가 그러한 예외에 해당되는 경우 이미 알고있을 것입니다. 이 경우 초기화되지 않은 자동 변수를 사용하는 것은 정의되지 않은 동작 입니다.

컴파일러는 정의되지 않은 동작에 대한 최적화를 통해 매우 공격적으로 바뀌 었으며 정의되지 않은 동작으로 인해 보안 결함이 발생하는 경우가 많이 있습니다. 가장 악명 높은 경우는 아마도 C ++ 컴파일 버그에 대한 답변 에서 언급 한 Linux 커널 null 포인터 검사 제거 입니까?정의되지 않은 동작에 대한 컴파일러 최적화는 유한 루프를 무한 루프로 바꿨습니다.

CERT의 위험한 최적화 및 인과 관계의 상실 ( 비디오 )을 읽을 수 있습니다 .

점점 더 컴파일러 작성자는 C 및 C ++ 프로그래밍 언어에서 정의되지 않은 동작을 활용하여 최적화를 향상시키고 있습니다.

종종 이러한 최적화는 개발자가 소스 코드에서 원인-효과 분석을 수행하는 능력, 즉 이전 결과에 대한 다운 스트림 결과의 종속성 분석을 방해합니다.

결과적으로 이러한 최적화는 소프트웨어의 인과 관계를 제거하고 소프트웨어 결함, 결함 및 취약점의 가능성을 높입니다.

특히 불확실한 값과 관련하여 C 표준 결함 보고서 451 : 초기화되지 않은 자동 변수의 불안정성으로 인해 일부 흥미로운 판독이 가능합니다. 아직 해결되지 않았지만 흔들리는 값 의 개념을 소개 합니다. 이는 의 결정 성이 프로그램을 통해 전파 될 수 있으며 프로그램의 다른 지점에서 다른 불확실한 값을 가질 수 있음을 의미합니다.

이런 일이 발생하는 예는 모르지만 지금은 배제 할 수 없습니다.

실제 결과, 예상 결과가 아님

임의의 값을 얻지 못할 수 있습니다. 컴파일러는 루프를 완전히 최적화 할 수 있습니다. 예를 들어,이 간단한 경우

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r ;
    }
}

clang은 그것을 멀리 최적화합니다 ( 살펴보십시오 ) :

updateEffect(int*):                     # @updateEffect(int*)
    retq

또는이 수정 된 경우와 같이 모두 0을 얻습니다.

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r%255 ;
    }
}

라이브보기 :

updateEffect(int*):                     # @updateEffect(int*)
    xorps   %xmm0, %xmm0
    movups  %xmm0, 64(%rdi)
    movups  %xmm0, 48(%rdi)
    movups  %xmm0, 32(%rdi)
    movups  %xmm0, 16(%rdi)
    movups  %xmm0, (%rdi)
    retq

이 두 경우 모두 완벽하게 수용 가능한 형태의 정의되지 않은 동작입니다.

우리가 Itanium에 있다면 트랩 값으로 끝날 수 있습니다 .

[...] 레지스터가 특별한 값이 아닌 값을 보유하는 경우, 몇 가지 명령을 제외하고 레지스터 트랩을 읽는다 [...]

다른 중요한 메모

UB Canaries 프로젝트에 언급 된 gcc와 clang차이 가 초기화되지 않은 메모리와 관련하여 정의되지 않은 동작을 어떻게 활용할 것인지에 대해 주목 하는 것이 흥미 롭습니다 . 기사 노트 ( 강조 광산 ) :

물론 우리는 그러한 기대는, 특정 컴파일러가해야 할 일이 무엇을 함께 할 수있는 언어 표준과는 아무런 모든 것을이 없다고 스스로 완전히 명확하게 할 필요가 컴파일러의 제공이 내키지 때문에 하나 악용하는 것을 UB 하거나 그들이 아직 악용하지 않았기 때문 입니다. 컴파일러 제공 업체의 실제 보증이 존재 하지 않는 한 아직 개발되지 않은 UB는 시한 폭탄이라고 말하고 싶습니다 .

Matthieu M.은 모든 C 프로그래머가 정의되지 않은 행동 # 2 / 3에 대해 알아야 할 사항도 지적합니다 . 그것은 다른 것들 중에서도 강조합니다 (내 것을 강조합니다 ).

실현하기 위해 중요하고 무서운 일이 있다는 것입니다 단지에 대한 어떤 정의되지 않은 동작을 기반으로 최적화 미래에 언제든지 버그 코드를 트리거하고 시작할 수 있습니다 . 인라인, 루프 언 롤링, 메모리 승격 및 기타 최적화는 계속 향상되고 있으며 기존 이유의 상당 부분은 위와 같은 2 차 최적화를 제공하는 것입니다.

나에게 이것은 컴파일러가 필연적으로 비난을 받기 때문에 부분적으로 불만족 스러우며 또한 C 코드의 거대한 몸체 가 폭발하기를 기다리는 지뢰 라는 것을 의미하기 때문 입니다.

완전성을 위해서 아마 구현 예를 들어, 잘 정의 된 정의되지 않은 동작을 선택할 수 있습니다 언급해야 GCC가 노동 조합을 통해 유형 말장난을 허용 하면서 C ++에서이 정의되지 않은 동작처럼 보인다 . 이 경우 구현시 문서화해야하며 일반적으로 이식성이 없습니다.


1
컴파일러 출력 예제의 경우 + (int) (PI / 3); 실생활의 예를 UB는 것을 잘, UB .

2
UB를 효과적으로 사용하면 훌륭한 해커의 상표가되었습니다. 이 전통은 현재 50 년 이상 지속되었습니다. 불행하게도, 컴퓨터는 나쁜 사람들 때문에 UB의 영향을 최소화해야합니다. OS가 사용자를 스스로 보호 할 수 없었을 때 나는 90 년대 UB 기계 코드 또는 포트 읽기 / 쓰기 등으로 멋진 일을하는 방법을 알아내는 것을 정말로 즐겼습니다.
sfdcfox

1
@sfdcfox 머신 코드 / 어셈블러에서 수행하는 경우 정의되지 않은 동작이 아닙니다 (전통적인 동작이 아닐 수도 있음).
Caleth

2
특정 어셈블리를 염두에두고 사용하고 호환되지 않는 C를 작성하지 마십시오. 그러면 누구나 휴대 할 수없는 특정 트릭을 사용하고 있음을 누구나 알 수 있습니다. 그리고 UB를 사용할 수 없다는 것을 의미하는 나쁜 사람들이 아닙니다 .Intel 등이 칩에 대한 트릭을하고 있습니다.
Caleth

2
@ 500-InternalServerError 일반적인 경우에는 쉽게 감지 할 수 없거나 전혀 감지 할 수 없으므로 허용 할 방법이 없습니다. 감지 할 수있는 문법 위반과 다릅니다. 또한 이론적으로 탐지 할 수있는 잘못 구성된 프로그램과 이론적으로 탐지 할 수없는 프로그램을 일반적으로 분리하는 잘못된 진단이 필요하지 않습니다.
Shafik Yaghmour

164

아뇨, 끔찍 해요

초기화되지 않은 변수를 사용하는 동작은 C 및 C ++에서 정의되지 않으며 이러한 체계가 바람직한 통계적 속성을 가질 가능성은 거의 없습니다.

"빠르고 더러운"난수 생성기를 원한다면 rand()최선의 선택입니다. 구현시 곱셈, 덧셈 및 모듈러스 만 수행됩니다.

내가 아는 가장 빠른 발생기는을 사용하도록 요구 uint32_t하여 의사 랜덤 변수의 유형으로 I, 사용

I = 1664525 * I + 1013904223

연속적인 값을 생성합니다. 당신의 초기 값을 선택할 수 있습니다 I(호출 당신의 공상 소요). 분명히 인라인으로 코딩 할 수 있습니다. 부호없는 유형의 표준 보증 랩 어라운드는 모듈러스 역할을합니다. (숫자 상수는 저명한 과학 프로그래머 인 Donald Knuth가 직접 선택합니다.)


9
제시 한 "선형 일치 성"생성기는 간단한 응용 프로그램에는 적합하지만 비 암호화 응용 프로그램에만 적합합니다. 동작을 예측할 수 있습니다. 예 : Don Knuth 자신의 " 선형 합동 암호화 해독 "(IEEE Transactions on Information Theory, Volume 31)
Jay

24
@Jay는 빠르고 더러운 단위 화 된 변수와 비교할 때? 이것은 훨씬 더 나은 솔루션입니다.
마이크 맥 마혼

2
rand()내 생각에 목적에 맞지 않으며 완전히 사용되지 않아야합니다. 매우 결함이 계속 필요 정말 없다, 그래서 매우 거의 빠른 용이성의 가장 큰에있는이 자유롭게 허가 다운받을 수 있습니다 일 훨씬 뛰어난 난수 생성기 (예 : 메르 센 트위스터)rand()
잭 Aidley

1
rand ()는 또 다른 끔찍한 문제가 있습니다. 내부 스레드라고 불리는 일종의 잠금을 사용하면 코드 속도가 크게 느려집니다. 최소한 재진입 버전이 있습니다. C ++ 11을 사용하면 임의 API가 필요한 모든 것을 제공합니다.
Marwan Burelle

4
공정하게 그는 좋은 난수 생성기인지 묻지 않았습니다. 그는 그것이 빠른지 물었다. 글쎄요, 아마도 금식 일 것입니다. 그러나 결과는 전혀 무작위가 아닙니다.
jcoder

42

좋은 질문!

정의되지 않은 것이 무작위임을 의미하지는 않습니다. 초기화되지 않은 전역 변수에서 얻을 수있는 값은 시스템이나 다른 응용 프로그램이 실행 중이었던 것으로 생각하십시오. 더 이상 사용하지 않는 메모리로 시스템이 수행하는 작업 및 / 또는 시스템 및 응용 프로그램이 생성하는 값의 종류에 따라 다음을 얻을 수 있습니다.

  1. 항상 동일합니다.
  2. 작은 값 집합 중 하나입니다.
  3. 하나 이상의 작은 범위에서 값을 가져옵니다.
  4. 16/32/64 비트 시스템의 포인터에서 2/4/8로 나눌 수있는 많은 값보기
  5. ...

얻을 수있는 값은 시스템 및 / 또는 응용 프로그램에 의해 임의의 비임의 값이 남는 것에 따라 달라집니다. 따라서 실제로 시스템에서 더 이상 사용 된 메모리를 지우지 않는 한 약간의 노이즈가 발생하지만 그릴 값 풀은 무작위가 아닙니다.

지역 변수는 자신의 프로그램 스택에서 직접 가져 오기 때문에 상황이 훨씬 나빠집니다. 다른 코드를 실행하는 동안 프로그램이 실제로 이러한 스택 위치를 작성할 가능성이 매우 높습니다. 나는이 상황에서 운이 매우 낮을 것으로 추정하고, 당신이 만드는 '무작위'코드 변경은이 운을 시도합니다.

무작위성 에 대해 읽으십시오 . 보시다시피 무작위성은 매우 구체적이며 구하기 어려운 속성입니다. (추천과 같이) 추적하기 어려운 것을 취하면 임의의 가치를 얻는다고 생각하는 것은 흔한 실수입니다.


7
... 그러면 코드를 완전히 소화시키는 모든 컴파일러 최적화가 생략됩니다.
중복 제거기

6 ... 디버그 및 릴리스에서 "임의"가 다릅니다. 정의되지 않은 것은 잘못하고 있음을 의미합니다.
Sql Surfer

권리. "undefined"! = "arbitrary"! = "random"으로 축약하거나 요약했습니다. 이러한 모든 "알 수 없음"속성은 서로 다릅니다.
fche

전역 변수는 명시 적으로 초기화되었는지 여부에 관계없이 정의 된 값을 갖도록 보장됩니다. 이것은 C ++C에서도 마찬가지 입니다.
브라이언 반덴버그

32

많은 좋은 답변이지만 다른 것을 추가하고 결정 론적 컴퓨터에서는 무작위가 없다는 점을 강조 할 수 있습니다. 이는 의사 -RNG에 의해 생성 된 숫자와 스택의 C / C ++ 로컬 변수 용으로 예약 된 메모리 영역에서 발견 된 "임의"숫자에 해당됩니다.

그러나 ... 결정적인 차이가 있습니다.

좋은 의사 난수 생성기에 의해 생성 된 숫자는 통계를 무작위로 그리는 것과 통계적으로 유사하게하는 속성을 갖습니다. 예를 들어 분포가 균일합니다. 사이클 길이가 길다 : 사이클이 반복되기 전에 수백만 개의 난수를 얻을 수 있습니다. 시퀀스는 자동 상관되지 않습니다. 예를 들어, 2, 3, 27 번째 숫자를 취하거나 생성 된 숫자의 특정 숫자를 보면 이상한 패턴이 나타나지 않습니다.

반대로, 스택에 남겨진 "임의"숫자에는 이러한 속성이 없습니다. 이들의 값과 명백한 임의성은 프로그램 구성 방법, 컴파일 방법 및 컴파일러가 최적화하는 방법에 전적으로 의존합니다. 예를 들어, 다음은 독립형 프로그램으로서의 아이디어 변형입니다.

#include <stdio.h>

notrandom()
{
        int r, g, b;

        printf("R=%d, G=%d, B=%d", r&255, g&255, b&255);
}

int main(int argc, char *argv[])
{
        int i;
        for (i = 0; i < 10; i++)
        {
                notrandom();
                printf("\n");
        }

        return 0;
}

Linux 컴퓨터에서 GCC 로이 코드를 컴파일하고 실행하면 다소 불쾌하게 결정됩니다.

R=0, G=19, B=0
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255

디스어셈블러로 컴파일 된 코드를 살펴보면 진행중인 작업을 자세히 재구성 할 수 있습니다. notrandom ()에 대한 첫 번째 호출은 이전에이 프로그램에서 사용되지 않은 스택 영역을 사용했습니다. 거기에 무엇이 있는지 아는 사람. 그러나 notrandom ()에 대한 호출 후에 printf ()에 대한 호출이 있습니다 (GCC 컴파일러는 실제로 putchar ()에 대한 호출에 대해 최적화하지만 결코 신경 쓰지 않습니다). 그러면 스택을 덮어 씁니다. 따라서 다음에 notrandom ()이 호출되면 스택은 putchar () 실행의 오래된 데이터를 포함하며 putchar ()은 항상 동일한 인수로 호출되므로이 오래된 데이터는 항상 동일합니다. 너무.

따라서이 동작에 대해 임의의 것은 없으며,이 방법으로 얻은 숫자는 잘 작성된 의사 난수 생성기의 바람직한 특성을 갖지 않습니다. 실제로 대부분의 실제 시나리오에서 그 값은 반복적이고 상관성이 높습니다.

사실, 다른 사람들과 마찬가지로, 나는이 아이디어를 "고성능 RNG"로 전달하려는 누군가를 해고 할 것을 진지하게 고려할 것입니다.


1
“결정 론적 컴퓨터에서는 아무것도 무작위가 아닙니다”— 이것은 사실이 아닙니다. 현대 컴퓨터는 당신이 생성 할 수 있도록 모든 종류의 센서가 포함 진정한 별도의 하드웨어 발전기하지 않고, 예측 불가능한 난수를. 현대 건축에서, 그 값 /dev/random은 종종 그러한 하드웨어 소스에서 시드되며 실제로는 "양자 노이즈"입니다.
Konrad Rudolph

2
그러나 그것은 결정론적인 컴퓨터가 아닙니다. 그렇지 않습니까? 이제 환경 입력에 의존하고 있습니다. 어쨌든, 이것은 초기화되지 않은 메모리의 "임의"비트에 대한 기존의 의사 -RNG에 대한 논의를 훨씬 뛰어 넘습니다. 또한 ... / dev / random에 대한 설명을 보면 구현자가 임의의 숫자가 암호화 방식으로 안전하다는 것을 이해하기 위해 얼마나 멀리 떨어져 있었는지 알 수 있습니다. 입력 소스가 순수하고 상관없는 양자 노이즈가 아니기 때문에 정확하게 오히려 약간의 무작위성만으로 잠재적으로 고도로 상관 된 센서 판독 값입니다. 꽤 느립니다.
빅토르 토스

29

정의되지 않은 동작은 프로그래머가 어떤 일이 있어도 불만을 제기 할 권리가 없기 때문에 컴파일러 작성자가 문제를 무시할 수 있음을 의미합니다.

이론적으로 UB 땅에 들어갈 때 어떤 일이 발생할 수 있습니까 ( 코를 날리는 데몬 포함 ) 일반적으로 컴파일러 작성자는 신경 쓰지 않으며 로컬 변수의 경우 값은 해당 시점의 스택 메모리에 있습니다 .

이것은 종종 내용이 "이상한"것이지만 고정되거나 약간 임의적이거나 가변적이지만 분명한 패턴을 가지고 있음을 의미합니다 (예 : 각 반복에서 값이 증가 함).

확실히 괜찮은 무작위 생성기가 될 수는 없습니다 .


28

정의되지 않은 동작은 정의되어 있지 않습니다. 그것은 당신이 정의되지 않은 값을 얻는다는 것을 의미하지는 않습니다. 그것은 프로그램이 무엇이든 있고 여전히 언어 사양을 충족 한다는 것을 의미합니다 .

좋은 최적화 컴파일러는

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

이것을 noop로 컴파일하십시오. 이것은 다른 대안보다 확실히 빠릅니다. 그것은 아무것도하지 않을 것이라는 단점이 있지만, 정의되지 않은 행동의 단점입니다.


3
컴파일러의 목적이 프로그래머가 도메인 요구 사항을 충족하는 실행 파일을 생성하는 데 도움이되는지 또는 목적이 C 표준의 최소 요구 사항과 일치하는 동작을 수행하는 가장 "효율적인"실행 파일을 생성하는 것이 목적에 달려 있습니다. 그러한 행동이 유용한 목적에 도움이 될 것인지 고려하십시오. 전자의 목적과 관련하여, 코드가 r, g, b에 대해 임의의 초기 값을 사용하거나 가능하다면 디버거 트랩을 트리거하는 것이 코드를 nop로 전환하는 것보다 더 유용합니다. 후자의 목표와 관련하여 ...
supercat

2
... 최적의 컴파일러는 어떤 입력이 위의 메소드를 실행하게 할지를 결정하고 이러한 입력을받을 때만 관련이있는 코드를 제거해야합니다.
supercat

1
@supercat 또는 그 목적은 C. 표준에 따라 효율적인 실행 파일을 생성하는 동시에 프로그래머가 규정 준수가 유용하지 않을 수있는 장소를 찾도록 도와주는 것일 수 있습니다. 컴파일러는 GCC와 같이 표준에 필요한 것보다 더 많은 진단을 제공함으로써 이러한 타협 목적을 충족시킬 수 있습니다 -Wall -Wextra.
Damian Yerrick

1
값이 정의되지 않았다고해서 주변 코드의 동작이 정의되지 않았다는 의미는 아닙니다. 컴파일러는 그 기능을 스누핑해서는 안됩니다. 주어진 두 가지 함수 호출은 반드시 입력해야합니다. 첫 번째는 0에서 255 사이의 세 개의 숫자로 호출되어야하며 두 번째는 true 또는 false 값으로 호출되어야합니다. "최적의 최적화 컴파일러"는 함수 매개 변수를 임의의 정적 값으로 최적화하여 변수를 완전히 제거 할 수 있지만 가능한 한 멀리 있습니다 (기능 자체가 특정 입력에서 noops로 축소되지 않는 한).
Dewi Morgan

@DewiMorgan-호출 된 함수가 "이 매개 변수 설정"유형이므로 입력이 매개 변수의 현재 값과 동일 할 때 거의 확실하게 noops로 줄어 듭니다.
Jules

18

아직 언급되지 않았지만 정의되지 않은 동작을 호출하는 코드 경로는 컴파일러가 원하는 모든 것을 수행 할 수 있습니다.

void updateEffect(){}

올바른 루프보다 빠르며 UB로 인해 완벽하게 준수합니다.


18

보안상의 이유로 프로그램에 할당 된 새 메모리를 정리해야합니다. 그렇지 않으면 정보를 사용할 수 있으며 암호가 한 응용 프로그램에서 다른 응용 프로그램으로 누출 될 수 있습니다. 메모리를 재사용 할 때만 0과 다른 값을 얻을 수 있습니다. 스택에서 이전 메모리 사용이 고정되어 있기 때문에 이전 값이 고정되었을 가능성이 큽니다.


13

특정 코드 예제는 아마도 당신이 기대하는 것을하지 않을 것입니다. 기술적으로 루프를 반복 할 때마다 r, g 및 b 값에 ​​대한 로컬 변수가 다시 생성되지만 실제로는 스택의 메모리 공간과 동일합니다. 따라서 각 반복마다 다시 무작위 화되지 않으며 r, g 및 b가 개별적으로 그리고 초기에 얼마나 랜덤한지에 관계없이 1000 색상 각각에 동일한 3 개의 값을 할당하게됩니다.

실제로 그것이 효과가 있었다면, 그것을 재 무작위 화하는 것에 대해 매우 궁금 할 것입니다. 내가 생각할 수있는 유일한 것은 그 스택 위에 피기 된 인터리브 인터럽트 일 것입니다. 아마도 레지스터가 루프에서 더 많이 재사용되는 실제 메모리 위치가 아닌 레지스터 변수로 내부 변수를 유지하는 내부 최적화는 특히 가시성 설정 기능이 특히 레지스터-배고픈 경우 트릭을 수행합니다. 여전히 무작위와는 거리가 멀다.


12

여기서 대부분의 사람들이 정의되지 않은 행동을 언급했습니다. undefined는 유효한 정수 값을 얻을 수 있음을 의미하며 (이 경우 랜드 함수 호출이 수행되지 않으므로) 더 빠릅니다. 그러나 실제로 사용하지 마십시오. 행운이 항상 당신과 함께하지 않기 때문에 나는 이것이 끔찍한 결과가 될 것이라고 확신합니다.


1
아주 좋은 지적입니다! 실용적이지만, 실제로 운이 필요한 방법 일 수 있습니다.
의미 문제

1
운이 전혀 없습니다. 컴파일러가 정의되지 않은 동작을 최적화하지 않으면 얻을 수있는 값은 완벽하게 결정적입니다 (= 프로그램, 입력, 컴파일러, 사용하는 라이브러리, 스레드가있는 스레드의 타이밍에 전적으로 의존 함). 문제는 이러한 값이 구현 세부 사항에 따라 달라지기 때문에 추론 할 수 없다는 것 입니다.
cmaster-monica reinstate

애플리케이션 스택과 분리 된 인터럽트 처리 스택이있는 운영 체제가없는 경우 인터럽트가 현재 스택 내용을 약간 넘어서서 메모리 내용을 자주 방해하므로 운이 좋을 수 있습니다.
supercat

12

정말 나빠! 나쁜 습관, 나쁜 결과. 치다:

A_Function_that_use_a_lot_the_Stack();
updateEffect();

함수 A_Function_that_use_a_lot_the_Stack()가 항상 동일한 초기화를 수행하면 스택에 동일한 데이터가 남습니다. 그 데이터는 우리가 부르는 것입니다 updateEffect(): 항상 같은 가치! .


11

나는 매우 간단한 테스트를 수행했으며 전혀 무작위가 아닙니다.

#include <stdio.h>

int main() {

    int a;
    printf("%d\n", a);
    return 0;
}

프로그램을 실행할 때마다 동일한 번호 ( 32767내 경우)를 인쇄합니다 . 그보다 훨씬 덜 무작위로 얻을 수는 없습니다. 이것은 아마도 런타임 라이브러리의 시작 코드가 스택에 남아있는 것으로 추정됩니다. 프로그램이 실행될 때마다 동일한 시작 코드를 사용하고 실행마다 프로그램에서 다른 점이 없으므로 결과는 완벽하게 일치합니다.


좋은 지적. 결과는 코드에서이 "임의"숫자 생성기가 호출되는 위치에 따라 크게 달라집니다. 무작위보다 예측할 수 없습니다.
NO_NAME

10

'무작위'의 의미에 대한 정의가 필요합니다. 합리적인 정의는 얻는 값과 상관 관계가 거의 없다는 것을 의미합니다. 그것은 당신이 측정 할 수있는 것입니다. 통제되고 재현 가능한 방식으로 달성하는 것도 사소한 일이 아닙니다. 따라서 정의되지 않은 행동은 확실히 당신이 찾고있는 것이 아닙니다.


7

초기화되지 않은 메모리를 "unsigned char *"유형 (예 :에서 반환 된 버퍼)을 사용하여 안전하게 읽을 수있는 특정 상황이 있습니다 malloc. 코드는 컴파일러가 창에서 인과 관계를 버리는 것에 대해 걱정할 필요없이 그러한 메모리를 읽을 수 있으며, 초기화되지 않은 데이터가 읽히지 않도록하는 것보다 메모리에 포함될 수있는 모든 코드를 준비하는 것이 더 효율적인 경우가 있습니다 ( 이것의 일반적인 예는memcpy 의미있는 데이터를 포함하는 모든 요소를 ​​개별적으로 복사하는 대신 부분적으로 초기화 된 버퍼를 사용하는 것입니다).

그러나 그러한 경우에도 바이트의 조합이 특히 까다로운 경우 항상 읽을 경우 해당 바이트 패턴이 생성됩니다 (그리고 특정 패턴이 프로덕션에서는 vexatious하지만 개발에서는 그렇지 않은 경우). 코드가 프로덕션 상태가 될 때까지 패턴이 나타나지 않습니다).

초기화되지 않은 메모리를 읽는 것은 시스템이 마지막으로 전원이 켜진 이후로 메모리가 실제로 거의 임의의 내용으로 쓰여지지 않았 음을 확인하는 임베디드 시스템에서 무작위 생성 전략의 일부로 유용 할 수 있습니다. 메모리에 사용되는 프로세스는 전원 켜기 상태가 반 무작위 방식으로 변하게합니다. 코드는 모든 장치가 항상 동일한 데이터를 생성하더라도 작동하지만 노드 그룹과 같이 각 노드의 초기 값을 절반으로 줄 수있는 "매우 무작위 적이 지 않은"생성기를 갖는 노드 그룹과 같이 임의의 고유 ID를 가능한 빨리 선택해야하는 경우 ID는 초기 임의성 소스를 전혀 사용하지 않는 것보다 낫습니다.


2
"바이트의 조합이 특히 어리석은 경우, 읽을 때 항상 해당 바이트 패턴을 얻을 것입니다."-해당 패턴에 대처하기 위해 코드를 작성할 때까지 더 이상 어리석지 않으며 나중에 다른 패턴을 읽을 수 있습니다.
Steve Jessop

@SteveJessop : 정확하게. 개발 대 생산에 대한 나의 선은 비슷한 개념을 전달하기위한 것이었다. 코드는 "일부 임의성이 좋을 수 있습니다"라는 모호한 개념을 넘어서 초기화되지 않은 메모리에있는 내용에 대해서는 신경 쓰지 않아야합니다. 프로그램 동작이 초기화되지 않은 메모리의 한 조각의 내용에 의해 영향을받는 경우 나중에 획득 한 조각의 내용이 그 영향을받을 수 있습니다.
supercat

5

다른 사람들이 말했듯이, 그것은 빠르지 만 무작위는 아닙니다.

대부분의 컴파일러가 로컬 변수를 위해 할 일은 스택에서 변수를위한 공간을 확보하는 것입니다.하지만 아무것도 설정하지 않아도됩니다 (표준은 필요하지 않다고 말하면서 생성하는 코드의 속도를 늦추는 이유는 무엇입니까?).

이 경우 얻을 수있는 값은 이전에 스택에 있던 것에 달려 있습니다. 이전에 100 개의 로컬 char 변수가 모두 'Q'로 설정된 함수를 호출 한 다음 함수를 호출하면 이 값을 반환하면 "랜덤"값이 memset()모두 'Q' 로 설정된 것처럼 작동하는 것입니다.

중요 하게이 기능을 사용하려는 예제 함수의 경우이 값을 읽을 때마다 변경되지 않으며 매번 동일합니다. 따라서 100 개의 별이 모두 동일한 색상과 가시성으로 설정됩니다.

또한 컴파일러 가이 값을 초기화해서는 안된다는 말은 없으므로 미래의 컴파일러가 그렇게 할 수 있습니다.

일반적으로 나쁜 생각은하지 마십시오. (실제로 많은 "영리한"코드 수준 최적화와 같은 ...)


2
UB로 인해 보장되는 것은 없지만 어떤 일 일어날 지에 대한 강력한 예측을하고 있습니다. 실제로는 사실이 아닙니다.
usr

3

다른 사람들이 이미 언급했듯이 이것은 정의되지 않은 동작 ( UB )이지만 "작동"할 수 있습니다.

다른 사람들이 이미 언급 한 문제를 제외하고는 다른 문제 (불이익)가 있습니다 .C 및 C ++ 이외의 다른 언어에서는 작동하지 않습니다. 나는이 질문이 C ++에 관한 것이라는 것을 알고 있지만, 좋은 C ++ 및 Java 코드가 될 코드를 작성할 수 있고 문제가되지 않는 이유는 무엇입니까? 언젠가 누군가가 그것을 다른 언어로 포팅하고 "매직 트릭" UB로 인한 버그를 검색해야 할 것입니다. 이런 경험은 특히 악몽 일 것입니다 (특히 경험이없는 C / C ++ 개발자의 경우).

여기에 다른 유사한 UB에 대한 질문이 있습니다. 이 UB에 대해 몰라도 이와 같은 버그를 찾으려고 상상해보십시오. 당신은 C / C 이러한 이상한 일들에 대해 더 읽고 싶다면 ++, 링크에서 질문에 대한 답변을 읽어 볼 GREAT 슬라이드 쇼를. 그것은 후드 아래에 무엇이 있고 어떻게 작동하는지 이해하는 데 도움이 될 것입니다. "매직"으로 가득 찬 또 다른 슬라이드 쇼가 아닙니다. 숙련 된 C / C ++ 프로그래머들조차도 이것으로부터 많은 것을 배울 수 있다고 확신합니다.


3

정의되지 않은 언어 동작에 대한 논리를 사용하는 것은 좋은 생각이 아닙니다 . 이 게시물에서 언급 / 논의 된 것 외에도 현대적인 C ++ 접근 방식 / 스타일을 사용하면 그러한 프로그램이 컴파일되지 않을 수 있습니다.

이것은 auto 의 장점을 포함하는 이전 게시물에서 언급되었습니다. 기능 기능 과 유용한 링크 .

https://stackoverflow.com/a/26170069/2724703

따라서 위의 코드를 변경하고 실제 유형을 auto로 바꾸면 프로그램도 컴파일되지 않습니다.

void updateEffect(){
    for(int i=0;i<1000;i++){
        auto r;
        auto g;
        auto b;
        star[i].setColor(r%255,g%255,b%255);
        auto isVisible;
        star[i].setVisible(isVisible);
    }
}

3

나는 당신의 사고 방식을 좋아합니다. 상자 밖에서 요 그러나 트레이드 오프는 실제로 가치가 없습니다. 메모리 런타임 트레이드 오프런타임에 정의되지 않은 동작을 포함하여 문제가 아닙니다. .

비즈니스 로직과 같은 "무작위"를 사용하고 있음을 알고 매우 불안한 ​​느낌을 주어야합니다. 나는 그것을하지 않았다.


3

7757초기화되지 않은 변수를 사용하려는 유혹을받는 모든 장소를 사용하십시오 . 소수 목록에서 무작위로 선택했습니다.

  1. 그것은 행동으로 정의된다

  2. 항상 0 인 것은 아닙니다

  3. 프라임

  4. 초기화되지 않은 변수만큼 통계적으로 임의적 일 수 있습니다.

  5. 컴파일 타임에 값이 알려지기 때문에 초기화되지 않은 변수보다 빠를 가능성이 높습니다.


비교를 위해,이 답변의 결과를 참조하십시오 stackoverflow.com/a/31836461/2963099
글렌 Teitelbaum

1

고려해야 할 또 하나의 가능성이 있습니다.

최신 컴파일러 (ahem g ++)는 코드가 지능적이어서 어떤 명령이 상태에 영향을 미치는지, 그렇지 않은지를 확인하고 명령이 상태에 영향을 미치지 않는다고 보장되면 g ++은 단순히 해당 명령을 제거합니다.

여기에 일어날 일이 있습니다. g ++은 읽기, 산술 수행, 저장, 본질적으로 가비지 값이 무엇인지, 더 많은 가비지를 생성한다는 것을 확실히 알 것입니다. 새로운 쓰레기가 이전 쓰레기보다 더 유용하다는 보장이 없기 때문에 간단하게 루프를 제거 할 수 있습니다. !!

이 방법은 유용하지만 여기에 내가하는 일이 있습니다. UB (Undefined Behaviour)와 rand () 속도를 결합하십시오.

물론 rand()s 실행을 줄이지 만 컴파일러를 원하지 않는 일을하지 않도록 혼합하십시오.

그리고 난 당신을 해고하지 않습니다.


컴파일러가 코드가 어리석은 짓을하고 그것을 결정할 수 있다고 생각하기가 매우 어렵다는 것을 알았습니다. 바람직하지 않은 코드 가 아닌 사용되지 않는 코드 를 최적화하는 것만 기대합니다 . 재현 가능한 테스트 사례가 있습니까? 어느 쪽이든, UB의 추천은 위험합니다. 게다가 GCC만이 유능한 컴파일러가 아니기 때문에 "현대"로 분류하는 것은 불공평합니다.
underscore_d

-1

무작위 화를 위해 초기화되지 않은 데이터를 사용하는 것이 올바르게 수행된다면 반드시 나쁜 것은 아닙니다. 실제로 OpenSSL은 PRNG를 시드하기 위해 정확하게이 작업을 수행합니다.

누군가 Valgrind가 초기화되지 않은 데이터 사용에 대해 불평하고 "고정" 하여 PRNG에 버그를 유발했기 때문에이 사용법은 잘 문서화되지 않은 것 같습니다. .

그래서 당신은 그것을 할 수 있지만, 당신이하고있는 일을 알고 코드를 읽는 사람이 이것을 이해하는지 확인해야합니다.


1
내 대답에서 볼 수 있듯이 오늘은 clang이 원하는 것을하지 않을 것이므로 정의되지 않은 동작으로 예상되는 컴파일러에 따라 다릅니다 .
Shafik Yaghmour

6
OpenSSL 은이 방법을 엔트로피 입력으로 사용했지만 그것이 좋은 것이라고 말하지 않습니다. 결국, 그들이 사용한 다른 엔트로피 소스는 PID 였습니다. 정확히 임의의 좋은 값은 아닙니다. 그런 나쁜 엔트로피 소스에 의존하는 누군가로부터 나는 그들의 다른 엔트로피 소스에 대한 좋은 판단을 기대하지 않을 것입니다. 현재 OpenSSL을 유지 관리하는 사람들이 더 밝기를 바랍니다.
cmaster-monica reinstate
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.