C ++ 프로그래머가 알아야 할 일반적인 정의되지 않은 동작은 무엇입니까?
다음과 같이 말합니다.
a[i] = i++;
C ++ 프로그래머가 알아야 할 일반적인 정의되지 않은 동작은 무엇입니까?
다음과 같이 말합니다.
a[i] = i++;
답변:
NULL
포인터 역 참조memcpy
겹치는 버퍼 복사에 사용 .int64_t i = 1; i <<= 72
정의되지 않음)int i; i++; cout << i;
)volatile
또는 이외의 유형의 객체 값 사용sig_atomic_t
신호를 수신 수신long int
#if
표현식 에서 정의 된 토큰을 동적으로 생성함수 매개 변수가 평가되는 순서는 지정되지 않은 동작 입니다. (이것은 정의되지 않은 동작 과 달리 프로그램이 충돌하거나 폭발하거나 피자를 주문하지 않습니다. .)
유일한 요구 사항은 함수를 호출하기 전에 모든 매개 변수를 완전히 평가해야한다는 것입니다.
이:
// The simple obvious one.
callFunc(getA(),getB());
이것과 동등 할 수 있습니다 :
int a = getA();
int b = getB();
callFunc(a,b);
아니면 이거:
int b = getB();
int a = getA();
callFunc(a,b);
둘 중 하나 일 수 있습니다. 컴파일러에 달려 있습니다. 부작용에 따라 결과가 중요 할 수 있습니다.
컴파일러는 표현식의 평가 부분을 자유롭게 재정렬 할 수 있습니다 (의미가 변경되지 않은 경우).
원래 질문에서 :
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
이중 점검 잠금. 그리고 하나의 쉬운 실수가 있습니다.
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
필자가 가장 좋아하는 것은 "템플릿 인스턴스화의 무한 재귀"입니다. 컴파일 타임에 정의되지 않은 동작이 발생하는 유일한 이유라고 생각하기 때문입니다.
undefined behavior 이외에도 구현이 정의 되지 않은 동작 도 있습니다. .
정의되지 않은 동작은 프로그램이 결과가 표준에 의해 지정되지 않은 작업을 수행 할 때 발생합니다.
구현-정의 된 행동은 결과가 표준에 의해 정의되지는 않지만 구현이 문서화되어야하는 프로그램에 의한 행동이다. 예를 들어, 스택 오버플로 질문의 "멀티 바이트 문자 리터럴"입니다. 컴파일에 실패한 C 컴파일러가 있습니까? .
구현 정의 동작은 이식을 시작할 때만 물지 만 (새로운 버전의 컴파일러로 업그레이드하는 것도 이식 중입니다!)
변수는 표현식에서 한 번만 (기술적으로 시퀀스 포인트 사이에서 한 번) 업데이트 될 수 있습니다.
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
다양한 환경 한계에 대한 기본 이해. 전체 목록은 C 사양의 5.2.4.1 섹션에 있습니다. 여기 몇 가지가 있습니다.
실제로 switch 문에 대한 1023 케이스 레이블의 한계에 약간 놀랐습니다. 생성 된 코드 / 렉스 / 파서가 상당히 쉽게 초과되는 것을 알 수 있습니다.
이러한 제한을 초과하면 정의되지 않은 동작 (크래쉬, 보안 결함 등)이 있습니다.
맞습니다. 이것이 C 사양에 의한 것임을 알고 있지만 C ++은 이러한 기본 지원을 공유합니다.
memcpy
겹치는 메모리 영역간에 복사하는 데 사용 합니다. 예를 들면 다음과 같습니다.
char a[256] = {};
memcpy(a, a, sizeof(a));
C ++ 03 표준에 따라 C 표준에 따라 동작이 정의되지 않습니다.
개요
1 / #include void * memcpy (void * 제한 s1, const void * 제한 s2, size_t n);
기술
2 / memcpy 함수는 s2가 가리키는 오브젝트에서 s1이 가리키는 오브젝트로 n 개의 문자를 복사합니다. 겹치는 객체간에 복사가 수행되면 동작이 정의되지 않습니다. 반환 값 3 memcpy 함수는 s1의 값을 반환합니다.
개요
1 #include void * memmove (void * s1, const void * s2, size_t n);
기술
2 memmove 함수는 s2가 가리키는 객체에서 s1이 가리키는 객체로 n 개의 문자를 복사합니다. s2가 가리키는 객체의 n 문자가 s1 및 s2가 가리키는 객체와 겹치지 않는 n 문자의 임시 배열로 먼저 복사 된 다음 임시 배열의 n 문자가 복사되는 것처럼 복사가 수행됩니다. s1이 가리키는 객체 보고
3 memmove 함수는 s1의 값을 반환합니다.
C ++에서 크기를 보장하는 유일한 유형은 char
입니다. 크기는 1입니다. 다른 모든 유형의 크기는 플랫폼에 따라 다릅니다.