(왜) 초기화되지 않은 변수 정의되지 않은 동작을 사용하고 있습니까?


82

만약 내가 가지고 있다면:

unsigned int x;
x -= x;

이 표현식 뒤에는 0이 x 되어야한다는 것은 분명 하지만, 내가 보는 모든 곳에서 그들은 이 코드 의 동작x(빼기 전까지) 값이 아니라 정의되지 않았다고 말합니다 .

두 가지 질문 :

  • 이 코드 의 동작 이 실제로 정의되지 않았습니까?
    (예 : 호환 시스템에서 코드가 충돌하거나 더 나빠질 수 있습니까?)

  • 그렇다면 C 는 여기서 0 이 되어야한다는 것이 완벽하게 분명 할 때 동작 이 정의되지 않았다고 말하는 이유 는 무엇입니까?x

    즉 여기에서 행동을 정의하지 않으면 어떤 이점이 있습니까?

분명히, 컴파일러는 간단하게 사용할 수있는 어떤 쓰레기 값이 변수 안에 "편리한"것으로 간주하고, 같이 일하는 것이 목적 ... 무슨 일이 그 방법 잘못?



3
여기에서 행동에 대한 특별한 경우를 정의하면 어떤 이점이 있습니까? 물론 @Mehrdad는 특정하고 드문 경우에서 변수를 초기화하지 않기를 원하기 때문에 모든 프로그램과 라이브러리를 더 크고 느리게 만들 수 있습니다.
Paul Tomblin

9
@ W'rkncacnter 나는 속임수라는 것에 동의하지 않습니다. 어떤 값을 취하는 지 여부에 관계없이 OP는 x -= x. 문제는 초기화되지 않은 값에 액세스하는 것이 UB인지입니다.
Mysticial

6
x = 0이라는 문장이 흥미 롭습니다. 일반적으로 어셈블리에서 xor x, x로 변환됩니다. 여기서하려는 것과 거의 동일하지만 빼기 대신 xor를 사용합니다.
0xFE

1
즉, 여기서 행동을 정의하지 않으면 어떤 이점이 있습니까? '-하나 이상의 변수에 의존하지 않는 값으로 표현의 무한대를 나열하지 않는 표준의 장점이 분명하다고 생각했을 것입니다. 동시에 @Paul, 표준에 대한 그러한 변경은 프로그램과 라이브러리를 더 크게 만들지 않습니다.
Jim Balter 2012 년

답변:


90

예,이 동작은 정의되지 않았지만 대부분의 사람들이 알고있는 것과는 다른 이유 때문입니다.

첫째, 단일화 된 값을 사용하는 것은 그 자체로 정의되지 않은 동작이 아니지만 값은 단순히 불확실합니다. 값이 유형에 대한 트랩 표현 인 경우 여기에 액세스하는 것은 UB입니다. 서명되지 않은 유형에는 트랩 표현이 거의 없으므로 그 쪽에서 상대적으로 안전합니다.

비헤이비어를 정의하지 않게 만드는 것은 변수의 추가 속성입니다. 즉, register주소를 사용하지 않는 " 선언 할 수있는"변수 입니다. 이러한 변수는 "초기화되지 않은"일종의 추가 상태가 있고 유형 도메인의 값에 해당하지 않는 실제 CPU 레지스터가있는 아키텍처가 있기 때문에 특별히 처리됩니다.

편집 : 표준의 관련 문구는 6.3.2.1p2입니다.

lvalue가 레지스터 스토리지 클래스로 선언 될 수있는 자동 저장 기간의 객체를 지정하고 (그 주소를 사용하지 않은 경우) 해당 객체가 초기화되지 않은 경우 (이니셜 라이저로 선언되지 않고 사용 전에 할당되지 않은 경우) ), 동작이 정의되지 않았습니다.

명확하게하기 위해 다음 코드 모든 상황에서 합법적입니다.

unsigned char a, b;
memcpy(&a, &b, 1);
a -= a;
  • 여기서 a및 의 주소를 b가져 오므로 값은 불확실합니다.
  • unsigned char불확실한 값이 지정되지 않은 트랩 표현이 없기 때문에의 모든 값 unsigned char이 발생할 수 있습니다.
  • 끝에는 값이 a 있어야합니다0 .

EDIT2 : ab지정되지 않은 값이 :

3.19.3 이 국제 표준이 어떤 경우에도 값이 선택되는 요건을 부과하지 않는 관련 유형의 불특정 값
유효 값


6
아마도 내가 뭔가를 놓치고 있지만 unsigneds는 확실히 함정 표현을 가질 수 있다고 생각합니다 . 그렇게 말하는 표준 부분을 가리킬 수 있습니까? §6.2.6.2 / 1에서 다음을 참조합니다. " 부호없는 char 이외의 부호 없는 정수 유형 의 경우, 객체 표현의 비트는 값 비트와 패딩 비트 (후자가 없을 필요가 없음)의 두 그룹으로 나뉩니다. ... 이것은 값 표현으로 알려져 있습니다. 모든 패딩 비트의 값은 지정되지 않습니다. "⁴⁴⁾ 패딩 비트의 일부 조합은 트랩 표현을 생성 할 수 있습니다"라는 주석과 함께 ⁴⁴⁾ ".
conio

6
"패딩 비트의 일부 조합은 예를 들어 한 패딩 비트가 패리티 비트 인 경우 트랩 표현을 생성 할 수 있습니다. 그럼에도 불구하고 유효한 값에 대한 산술 연산은 다음과 같은 예외 조건의 일부가 아닌 다른 트랩 표현을 생성 할 수 없습니다. 오버플로이며 이는 서명되지 않은 유형에서는 발생할 수 없습니다. " - 잘 됐네요 일단 우리가 작업에 유효한 값을 가지고 있지만, 불확정 값이 있습니다 (예 : 패리티 비트 세트의 잘못을) 초기화되기 전에 트랩 표현합니다.
conio

4
@conio 이외의 모든 유형에 대해 정확 unsigned char하지만이 대답은 unsigned char. 참고 : 엄격하게 준수하는 프로그램은를 sizeof(unsigned) * CHAR_BIT기반으로 UINT_MAX특정 구현이 unsigned. 해당 프로그램이 결정을 내린 후에는이 답변이에서 수행하는 작업을 정확하게 수행 할 수 있습니다 unsigned char.

4
@JensGustedt : memcpy주의가 산만 하지 않습니까? 예를 들어 *&a = *&b;.
R .. GitHub의 STOP 돕기 ICE

4
@R .. 더 이상 확실하지 않습니다. C위원회의 메일 링리스트에 대한 논의가 진행 중이며,이 모든 것이 큰 엉망인 것 같습니다. 즉 의도 한 행동과 실제로 작성된 행동 사이의 큰 차이입니다. 그러나 분명한 것은 메모리에 액세스하는 것이 도움이 unsigned char되므로에 대한 것이 명확하지 않다는 memcpy것입니다 *&. 이 문제가 해결되면보고하겠습니다.
Jens Gustedt 2015

24

C 표준은 컴파일러에게 최적화를 수행 할 수있는 많은 권한을 제공합니다. 초기화되지 않은 메모리가 임의의 비트 패턴으로 설정되고 모든 작업이 작성된 순서대로 수행되는 프로그램의 순진한 모델을 가정하면 이러한 최적화의 결과는 놀라 울 수 있습니다.

참고 : 다음 예제는 x주소를 사용하지 않았기 때문에 유효하므로 "등록과 유사"합니다. 유형에 x트랩 표현이있는 경우에도 유효합니다 . 서명되지 않은 유형의 경우는 드물며 (최소한 1 비트의 스토리지를 "낭비"해야하며 문서화해야 함) unsigned char. x서명 된 유형이있는 경우 구현은-(2 n-1 -1)과 2 n-1 -1 사이의 숫자가 아닌 비트 패턴을 트랩 표현으로 정의 할 수 있습니다. Jens Gustedt의 답변을 참조하십시오 .

레지스터가 메모리보다 빠르기 때문에 컴파일러는 레지스터를 변수에 할당하려고합니다. 프로그램은 프로세서에있는 레지스터보다 더 많은 변수를 사용할 수 있기 때문에 컴파일러는 레지스터 할당을 수행하여 다른 시간에 동일한 레지스터를 사용하는 다른 변수를 만듭니다. 프로그램 조각을 고려하십시오

unsigned x, y, z;   /* 0 */
y = 0;              /* 1 */
z = 4;              /* 2 */
x = - x;            /* 3 */
y = y + z;          /* 4 */
x = y + 1;          /* 5 */

라인 3이 평가 될 때 x아직 초기화되지 않았으므로 (컴파일러의 이유) 라인 3은 컴파일러가 알아낼만큼 똑똑하지 않은 다른 조건으로 인해 발생할 수없는 일종의 우연이어야합니다. z4 번 줄 이후 에는 사용 x되지 않고 5 번 줄 이전에는 사용되지 않으므로 두 변수에 동일한 레지스터를 사용할 수 있습니다. 따라서이 작은 프로그램은 레지스터에 대한 다음 작업으로 컴파일됩니다.

r1 = 0;
r0 = 4;
r0 = - r0;
r1 += r0;
r0 = r1;

의 최종 값 x은의 최종 값 r0이고의 최종 값 y은의 최종 값입니다 r1. 이러한 값은 x = -3 및 y = -4이며 x제대로 초기화 된 경우 발생하는 5와 4가 아닙니다 .

보다 정교한 예를 보려면 다음 코드 조각을 고려하십시오.

unsigned i, x;
for (i = 0; i < 10; i++) {
    x = (condition() ? some_value() : -x);
}

컴파일러 condition가 부작용이 없음을 감지했다고 가정합니다 . condition수정하지 않기 때문에 x컴파일러는 루프를 통한 첫 번째 실행 x이 아직 초기화되지 않았으므로 액세스 할 수 없음을 알고 있습니다 . 따라서 루프 본문의 첫 번째 실행은와 동일 x = some_value()하므로 조건을 테스트 할 필요가 없습니다. 컴파일러는 여러분이 작성한 것처럼이 코드를 컴파일 할 수 있습니다.

unsigned i, x;
i = 0; /* if some_value() uses i */
x = some_value();
for (i = 1; i < 10; i++) {
    x = (condition() ? some_value() : -x);
}

이 컴파일러의 내부 모델링 될 수있는 방법에 따라 값이 있음을 고려하는 것입니다 x편리하다 어떤 값 만큼으로 x초기화되지 않습니다. 초기화되지 않은 변수가 정의되지 않은 경우의 동작은 단순히 지정되지 않은 값을 갖는 변수가 아니라 변수이기 때문에 컴파일러는 편리한 값 간의 특별한 수학적 관계를 추적 할 필요가 없습니다. 따라서 컴파일러는 위의 코드를 다음과 같이 분석 할 수 있습니다.

  • 첫 번째 루프 반복 중에는 x시간 -x이 평가 될 때까지 초기화되지 않습니다.
  • -x 정의되지 않은 동작이 있으므로 그 값은 무엇이든 편리합니다.
  • 최적화 규칙이 적용 되므로이 코드를 .condition ? value : valuecondition; value

질문의 코드와 마주 쳤을 때이 동일한 컴파일러 x = - x는 평가 될 때 의 값 -x이 무엇이든 편리 하다는 것을 분석 합니다. 따라서 할당을 최적화 할 수 있습니다.

위에서 설명한대로 동작하는 컴파일러의 예를 찾지는 않았지만 좋은 컴파일러가 시도하는 일종의 최적화입니다. 나는 하나를 만나도 놀라지 않을 것입니다. 다음은 프로그램이 충돌하는 컴파일러의 덜 그럴듯한 예입니다. (어떤 종류의 고급 디버깅 모드에서 프로그램을 컴파일하는 것은 그다지 믿을 수없는 일이 아닙니다.)

이 가상 컴파일러는 다른 메모리 페이지의 모든 변수를 매핑하고 페이지 속성을 설정하여 초기화되지 않은 변수에서 읽는 것이 디버거를 호출하는 프로세서 트랩을 유발하도록합니다. 변수에 대한 모든 할당은 먼저 해당 메모리 페이지가 정상적으로 매핑되었는지 확인합니다. 이 컴파일러는 고급 최적화를 수행하지 않습니다. 디버깅 모드에 있으며 초기화되지 않은 변수와 같은 버그를 쉽게 찾을 수 있습니다. 시 x = - x평가되고, 우측 트랩 발생 디버거 위로 발사.


+1 좋은 설명, 표준은 그 상황을 특별히 처리하고 있습니다. 그 이야기를 계속하려면 아래 내 대답을 참조하십시오. (주석으로하기에는 너무 깁니다).
Jens Gustedt

@JensGustedt 오, 귀하의 답변은 내가 (및 다른 사람들)이 놓친 매우 중요한 점을 만듭니다. 유형에 서명되지 않은 유형의 경우 적어도 1 비트를 "낭비"해야하는 트랩 값이 없으면 x초기화되지 않은 값이 있지만 액세스 동작은 x에 레지스터와 같은 동작이없는 경우 정의됩니다.
Gilles 'SO- 사악한 중지'

@Gilles : 적어도 clang은 당신이 언급 한 종류의 최적화를 만듭니다 : (1) , (2) , (3) .
Vlad

1
clang이 그런 방식으로 일을 처리하는 데 어떤 실질적인 이점이 있습니까? 다운 스트림 코드에서 값을 사용하지 않는 경우 x해당 값이 정의되었는지 여부에 관계없이 모든 작업을 생략 할 수 있습니다. 예를 들어 다음 코드가 if (volatile1) x=volatile2; ... x = (x+volatile3) & 255;0을 산출 x한 경우에 포함 할 수있는 0-255 값에 똑같이 만족 volatile1한다면 프로그래머가 불필요한 쓰기를 생략 할 수있는 구현 x은 다음과 같은 것보다 높은 품질로 간주되어야한다고 생각합니다. 행동 할 것입니다 ...
supercat

...이 경우 완전히 예측할 수없는 방식으로. 이 경우 구현 정의 함정을 안정적으로 발생시키는 구현은 특정 목적을 위해 아직 더 높은 품질로 간주 될 수 있지만 완전히 예측할 수없는 방식으로 작동하는 것은 거의 모든 목적에 대해 가장 낮은 품질의 작동 방식처럼 보입니다.
supercat

16

예, 프로그램이 충돌 할 수 있습니다. 예를 들어, CPU 인터럽트를 유발할 수있는 트랩 표현 (처리 할 수없는 특정 비트 패턴)이있을 수 있으며, 처리되지 않으면 프로그램이 중단 될 수 있습니다.

(최근 C11 초안의 6.2.6.1에 따르면) 특정 개체 표현은 개체 유형의 값을 나타낼 필요가 없습니다. 객체의 저장된 값에 이러한 표현이 있고 문자 유형이없는 lvalue 표현식에서 읽는 경우 동작이 정의되지 않습니다. 이러한 표현이 문자 유형이없는 lvalue 표현식에 의해 객체의 전체 또는 일부를 수정하는 부작용에 의해 생성되는 경우 동작은 정의되지 않습니다 .50) 이러한 표현을 트랩 표현이라고합니다.

(이 설명 unsigned int은 실제 시스템에서는 드문 트랩 표현을 가질 수있는 플랫폼에만 적용됩니다 . 표준의 현재 표현으로 이어지는 대체 및 아마도 더 일반적인 원인에 대한 자세한 내용과 참조는 주석을 참조하십시오.)


3
@VladLazarenko : 이것은 특정 CPU가 아니라 C에 관한 것입니다. 누구나 그것을 미치게 만드는 정수에 대한 비트 패턴을 가진 CPU를 사소하게 설계 할 수 있습니다. 레지스터에 "미친 비트"가있는 CPU를 고려하십시오.
David Schwartz

2
그렇다면 정수와 x86의 경우 동작이 잘 정의되어 있다고 말할 수 있습니까?

3
글쎄요, 이론적으로 당신은 (x86에서) 28 비트 정수만을 사용하기로 결정한 컴파일러를 가질 수 있고, 각각의 더하기, 곱하기 (등)를 처리하기 위해 특정 코드를 추가하고,이 4 비트가 사용되지 않는지 (아니면 SIGSEGV를 내 보냅니다) ). 초기화되지 않은 값으로 인해이 문제가 발생할 수 있습니다.
eq-

4
누군가가 문제를 이해하지 못하기 때문에 다른 사람을 모욕하는 것이 싫어요. 행동이 정의되지 않았는지 여부는 전적으로 표준이 말하는 문제입니다. 아, 그리고 eq의 시나리오에 대해 실용적인 것은 전혀 없습니다. 그것은 완전히 인위적인 것입니다.
Jim Balter 2012 년

7
@Vlad Lazarenko : Itanium CPU에는 각 정수 레지스터에 대한 NaT (Not a Thing) 플래그가 있습니다. NaT 플래그는 추측 실행을 제어하는 ​​데 사용되며 사용 전에 제대로 초기화되지 않은 레지스터에 남아있을 수 있습니다. NaT 비트 세트를 사용하여 이러한 레지스터에서 읽으면 예외가 발생합니다. blogs.msdn.com/b/oldnewthing/archive/2004/01/19/60162.aspx
Nordic Mainframe

13

(이 답변은 C 1999를 다룹니다. C 2011의 경우 Jens Gustedt의 답변을 참조하십시오.)

C 표준은 초기화되지 않은 자동 저장 기간의 객체 값을 사용하는 것이 정의되지 않은 동작이라고 말하지 않습니다. C 1999 표준은 6.7.8 10에서 "자동 저장 기간이있는 객체가 명시 적으로 초기화되지 않으면 그 값은 불확실합니다"라고 말합니다. (이 단락은 정적 객체가 초기화되는 방법을 정의하므로 우리가 염려하는 유일한 초기화되지 않은 객체는 자동 객체입니다.)

3.17.2는 "미정 값"을 "미지정 값 또는 트랩 표현"으로 정의합니다. 3.17.3은 "미지정 값"을 "이 국제 표준이 어떤 경우에도 값이 선택되는 요건을 부과하지 않는 관련 유형의 유효한 값"으로 정의합니다.

따라서 초기화되지 unsigned int x않은 값이 지정 되지 않은 경우 x -= x0을 생성해야합니다. 그것은 함정 표현인지에 대한 의문을 남깁니다. 트랩 값에 액세스하면 6.2.6.1 5에 따라 정의되지 않은 동작이 발생합니다.

부동 소수점 숫자의 신호 NaN과 같은 일부 유형의 객체에는 트랩 표현이있을 수 있습니다. 그러나 부호없는 정수는 특별합니다. 6.2.6.2에 따라 unsigned int의 N 값 비트는 각각 2의 거듭 제곱을 나타내고 값 비트의 각 조합은 0에서 2 N -1 사이의 값 중 하나를 나타냅니다 . 따라서 부호없는 정수는 패딩 비트의 일부 값 (예 : 패리티 비트)으로 인해 트랩 표현 만 가질 수 있습니다.

대상 플랫폼에서 unsigned int에 패딩 비트가없는 경우 초기화되지 않은 unsigned int는 트랩 표현을 가질 수 없으며 해당 값을 사용하면 정의되지 않은 동작이 발생할 수 없습니다.


x함정 표현이 있다면 함정일 x -= x수도 있지요? 그래도 추가 비트가없는 부호없는 정수를 가리키는 +1은 동작을 정의 했어야합니다. 이것은 분명히 다른 답변과 반대이며 (인용문에 따르면) 표준이 의미하는 것 같습니다.
user541686 aug

예, 유형에 x트랩 표현이 있으면 트랩 x -= x할 수 있습니다. 단순히 x값으로 사용 하더라도 함정이 될 수 있습니다. ( xlvalue 로 사용 하는 것이 안전합니다 . 객체에 쓰는 것은 그 안에있는 트랩 표현의 영향을받지 않습니다.)
Eric Postpischil

부호없는 형식은 거의 트랩 표현이 없다
옌스 Gustedt

Raymond Chen을 인용 하여 "ia64에서 각 64 비트 레지스터는 실제로 65 비트입니다. 추가 비트는"아님 "을 나타내는"NaT "라고합니다.이 비트는 레지스터에 유효한 값이 없을 때 설정됩니다. 그것을 부동 소수점 NaN의 정수 버전으로 생각하십시오. ... 값이 NaT 인 레지스터가 있고 잘못된 방식으로 숨을 쉬는 경우 (예 : 값을 메모리에 저장하려고) 프로세서는 STATUS_REG_NAT_CONSUMPTION 예외를 발생시킵니다. " 즉, 트랩 비트는 값을 완전히 벗어날 수 있습니다.
건배와 hth. - 알프

−1 "대상 플랫폼에서 unsigned int에 패딩 비트가없는 경우 초기화되지 않은 unsigned int는 트랩 표현을 가질 수 없으며 해당 값을 사용하면 정의되지 않은 동작이 발생할 수 없습니다." x64 NaT 비트와 같은 체계를 고려하지 않습니다.
건배와 hth. - 알프

11

예, 정의되지 않았습니다. 코드가 충돌 할 수 있습니다. C는 일반 규칙에 예외를 적용 할 특별한 이유가 없기 때문에 동작이 정의되지 않았다고 말합니다. 장점은 정의되지 않은 동작의 다른 모든 경우와 동일한 장점입니다. 컴파일러는이 작업을 수행하기 위해 특수 코드를 출력 할 필요가 없습니다.

분명히, 컴파일러는 변수 내에서 "편리한"것으로 간주되는 가비지 값을 간단히 사용할 수 있으며 의도 한대로 작동합니다 ... 그 접근 방식에 어떤 문제가 있습니까?

왜 그런 일이 일어나지 않는다고 생각하십니까? 그것이 정확히 취해진 접근 방식입니다. 컴파일러가 작동하도록 할 필요는 없지만 실패하도록 만들 필요는 없습니다.


1
그러나 컴파일러는 이것에 대한 특별한 코드를 가질 필요가 없습니다. 단순히 공간을 할당하고 (항상 그렇듯이) 변수를 초기화 하지 않으면 올바른 동작을 제공합니다. 특별한 논리가 필요하지 않다고 생각합니다.
user541686

7
1) 물론입니다. 그러나 나는 그것을 더 좋게 만들 어떤 논쟁도 생각할 수 없습니다. 2) 플랫폼은 초기화되지 않은 메모리의 가치를 신뢰할 수 없음을 알고 있으므로 자유롭게 변경할 수 있습니다. 예를 들어 백그라운드에서 초기화되지 않은 메모리를 제로화하여 필요할 때 사용할 준비가 된 페이지를 제로화 할 수 있습니다. (이런 일이 발생하는지 고려하십시오. 1) 뺄 값을 읽고 3을 얻습니다. 2) 페이지가 초기화되지 않았기 때문에 0이되고 값을 0으로 변경합니다. 3) 원자 뺄셈을 수행하여 페이지를 할당하고 가치 -3. 죄송합니다.)
David Schwartz

2
-1 귀하의 주장에 대한 정당성을 제공하지 않기 때문입니다. 컴파일러가 메모리 위치에 기록 된 값만 사용한다고 예상하는 것이 유효한 상황이 있습니다.
옌스 Gustedt

1
@JensGustedt : 귀하의 의견을 이해할 수 없습니다. 명확히 해 주시겠습니까?
David Schwartz

3
참조하지 않고 일반적인 규칙이 있다고 주장하기 때문입니다. 따라서 그것은 내가 그렇게 기대하는 것이 아닌 "권한에 의한 증명"의 시도 일 뿐이다. 그리고 이것이 왜 불특정 한 가치가 될 수 없는지를 효과적으로 논하지 않기 때문입니다. 이것이 일반적인 경우에 UB 인 유일한 이유 x는으로 선언 될 수 있기 register때문입니다. 즉, 주소가 사용되지 않기 때문입니다. 그 사실을 알고 있었는지는 모르겠지만 (효과적으로 숨기고 있었다면) 정답은 반드시 언급해야합니다.
Jens Gustedt

6

초기화되지 않았거나 다른 이유로 인해 불확실한 값을 보유하는 모든 유형의 변수에 대해 해당 값을 읽는 코드에 다음이 적용됩니다.

  • 변수가 자동 저장 기간을 가지고 경우 그 주소가 촬영이없는 코드는 항상 정의되지 않은 동작 [1]를 호출합니다.
  • 그렇지 않으면 시스템이 주어진 변수 유형에 대한 트랩 표현을 지원하는 경우 코드는 항상 정의되지 않은 동작을 호출합니다 [2].
  • 그렇지 않고 트랩 표현이없는 경우 변수는 지정되지 않은 값을 사용합니다. 이 지정되지 않은 값이 변수를 읽을 때마다 일관성이 있다는 보장은 없습니다. 그러나 트랩 표현이 아님을 보장하므로 정의되지 않은 동작을 호출하지 않도록 보장됩니다 [3].

    이 값은 프로그램 충돌을 일으키지 않고 안전하게 사용할 수 있지만, 이러한 코드는 트랩 표현이있는 시스템으로 이식 할 수 없습니다.


[1] : C11 6.3.2.1 :

lvalue가 레지스터 스토리지 클래스로 선언 될 수있는 자동 저장 기간의 객체를 지정하고 (그 주소를 사용하지 않은 경우) 해당 객체가 초기화되지 않은 경우 (이니셜 라이저로 선언되지 않고 사용 전에 할당되지 않은 경우) ), 동작이 정의되지 않았습니다.

[2] : C11 6.2.6.1 :

특정 객체 표현은 객체 유형의 값을 나타낼 필요가 없습니다. 객체의 저장된 값에 이러한 표현이 있고 문자 유형이없는 lvalue 표현식에서 읽는 경우 동작이 정의되지 않습니다. 이러한 표현이 문자 유형이없는 lvalue 표현에 의해 객체의 전체 또는 일부를 수정하는 부작용에 의해 생성되는 경우 동작은 정의되지 않습니다 .50) 이러한 표현을 트랩 표현이라고합니다.

[3] C11 :

3.19.2
불확정 값
불특정 값 또는 트랩 표현 중

3.19.3
불특정 값
유효 값이 국제 표준은 어떤 경우에도 값이 선택되는 요건을 부과하지 않는 관련 유형의 유효 값
비고 불특정 값은 트랩 표현이 될 수 없습니다.

3.19.4
트랩 표현
객체 유형의 값을 나타낼 필요가없는 객체 표현


C 추상 기계가 트랩 표현을 가질 수 있기 때문에 이것이 "항상 정의되지 않은 동작"으로 해결된다고 주장합니다. 구현에서이를 사용하지 않는다고해서 코드가 정의되지는 않습니다. 사실 엄격한 판독은 트랩 표현이 하드웨어에 있어야한다고 주장하지 않을 것입니다. 컴파일러가 특정 비트 패턴을 트랩으로 결정할 수없는 이유를 모르겠습니다. 변수를 읽을 때마다 확인하십시오. UB를 호출합니다.
Vality 2017 년

2
@Vality 현실 세계에서 모든 컴퓨터의 99.9999 %는 트랩 표현이없는 2의 보완 CPU입니다. 따라서 트랩 표현이 표준이 아니며 실제 컴퓨터에서 동작을 논의하는 것은 매우 관련이 있습니다. 매우 이국적인 컴퓨터가 표준이라고 가정하는 것은 도움이되지 않습니다. 실제 세계에서 트랩 표현은 매우 드물기 때문에 표준에 트랩 표현이라는 용어가있는 것은 대부분 1980 년대부터 상속 된 표준 결함으로 간주됩니다. 보완 및 부호 및 크기 컴퓨터에 대한 지원도 마찬가지입니다.
Lundin

2
그건 그렇고, 이것이 stdint.hC의 네이티브 유형 대신 항상 사용해야 하는 훌륭한 이유 입니다. stdint.h2의 보수를 적용하고 패딩 비트를 적용하지 않기 때문입니다. 즉, stdint.h유형이 쓰레기로 가득 차는 것은 허용되지 않습니다.
Lundin

2
다시 결함 보고서에 대한위원회의 응답은 다음과 같습니다. "질문 2에 대한 대답은 불확실한 값에 대해 수행되는 모든 작업이 결과적으로 불확실한 값을 갖게된다는 것입니다." 그리고 "질문 3에 대한 대답은 라이브러리 함수가 불확실한 값에 사용될 때 정의되지 않은 동작을 보일 것이라는 것입니다."
Antti Haapala

2
DR 451 및 260
Antti Haapala

2

많은 답변이 초기화되지 않은 레지스터 액세스를 트랩하는 프로세서에 초점을 맞추고 있지만, UB를 악용하려는 특별한 노력을하지 않는 컴파일러를 사용하여 이러한 트랩이없는 플랫폼에서도 기발한 동작이 발생할 수 있습니다. 코드를 고려하십시오.

volatile uint32_t a,b;
uin16_t moo(uint32_t x, uint16_t y, uint32_t z)
{
  uint16_t temp;
  if (a)
    temp = y;
  else if (b)
    temp = z;
  return temp;  
}

로드 및 저장 이외의 모든 명령어가 32 비트 레지스터에서 작동하는 ARM과 같은 플랫폼 용 컴파일러는 다음과 같은 방식으로 코드를 합리적으로 처리 할 수 ​​있습니다.

volatile uint32_t a,b;
// Note: y is known to be 0..65535
// x, y, and z are received in 32-bit registers r0, r1, r2
uin32_t moo(uint32_t x, uint32_t y, uint32_t z)
{
  // Since x is never used past this point, and since the return value
  // will need to be in r0, a compiler could map temp to r0
  uint32_t temp;
  if (a)
    temp = y;
  else if (b)
    temp = z & 0xFFFF;
  return temp;  
}

휘발성 읽기 중 하나가 0이 아닌 값을 생성하면 r0은 0 ... 65535 범위의 값으로로드됩니다. 그렇지 않으면 0..65535 범위의 값이 아닐 수있는 함수가 호출 될 때 보유한 모든 값 (즉, x에 전달 된 값)을 생성합니다. 표준에는 유형이 uint16_t이지만 값이 0..65535 범위를 벗어난 값의 동작을 설명하는 용어가 없습니다. 단, 그러한 동작을 생성 할 수있는 모든 동작이 UB를 호출한다는 점이 다릅니다.


흥미 롭군. 그래서 받아 들여진 대답이 틀렸다는 말입니까? 아니면 이론상 옳지 만 실제로 컴파일러가 이상한 일을 할 수 있다고 말하는가?
user541686

@Mehrdad : UB가 없을 때 가능한 범위를 넘어서는 동작을 구현하는 것이 일반적입니다. 표준이 "할당 된"비트가 최악의 경우 지정되지 않은 방식으로 동작하지만 비 결정적으로 동작하는 추가 상위 비트 (예 : 위 함수의 결과는 유형의 변수에 저장되며 uint16_t해당 변수는 때때로 123으로 읽히고 때로는 6553623으로 읽힐 수 있습니다). 결과가 무시되면 ...
supercat

... 또는 읽을 수있는 가능한 모든 방법으로 요구 사항을 충족하는 최종 결과를 얻을 수있는 방식으로 사용됩니다. 부분적으로 불확실한 값의 존재는 문제가되지 않아야합니다. 다른 한편으로, 표준이 행동 요구 사항을 부과하는 모든 상황에서 부분적으로 결정되지 않은 값의 존재를 허용하는 표준에는 아무것도 없습니다.
supercat

당신이 설명하는 것은 받아 들여진 대답에 정확히있는 것 같습니다. 변수 가으로 선언 될 수 있다면 register행동을 잠재적으로 정의되지 않게 만드는 추가 비트가있을 수 있습니다. 그게 정확히 당신이 말하는 거죠?
user541686

@Mehrdad : 허용되는 답변은 레지스터에 추가 "초기화되지 않은"상태가있는 아키텍처에 초점을 맞추고 초기화되지 않은 레지스터가로드되면 트랩합니다. 이러한 아키텍처는 존재하지만 일반적이지 않습니다. 일반적인 하드웨어가 C 표준에서 고려하는 영역을 벗어나는 동작을 나타낼 수 있지만 컴파일러가 혼합에 자체 추가 불안정성을 추가하지 않으면 유용하게 제한 되는 시나리오를 설명합니다 . 예를 들어 함수에 수행 할 작업을 선택하는 매개 변수가 있고 일부 작업은 유용한 데이터를 반환하지만 다른 작업은 그렇지 않은 경우 ...
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.