a[a[0] = 1은 정의되지 않은 동작을 생성합니까?
이 C99 코드는 정의되지 않은 동작을 생성합니까?
#include <stdio.h>
int main() {
int a[3] = {0, 0, 0};
a[a[0]] = 1;
printf("a[0] = %d\n", a[0]);
return 0;
}
세 in 。a[a[0]] = 1;
,a[0]
읽기도 하고 수정하기도 합니다.
ISO/IEC 9899의 n1124 초안을 보았습니다.(6.5 식에서) 다음과 같이 되어 있습니다.
이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 객체는 식을 평가하여 저장된 값을 한 번만 수정해야 합니다.또한 이전 값은 저장할 값을 결정하기 위해서만 읽어야 한다.
수정할 개체 자체를 결정하기 위해 개체를 읽는 것은 언급하지 않습니다.따라서 이 문장은 정의되지 않은 동작을 발생시킬 수 있습니다.
그런데 이상하게 느껴져요.이것이 실제로 정의되지 않은 동작을 발생시키나요?
(다른 ISO C 버전에서의 이 문제에 대해서도 알고 싶습니다).
이전 값은 저장할 값을 결정하기 위해서만 읽어야 합니다.
이는 다소 모호하고 혼란을 야기하기 때문에 C11은 이를 버리고 새로운 시퀀스 모델을 도입했습니다.
즉, 이전 값을 읽는 것이 새 값을 쓰는 것보다 더 빨리 발생하도록 보장된다면 괜찮습니다.그렇지 않으면 UB입니다.물론 새로운 값을 작성하기 전에 계산해야 합니다.
(물론 방금 쓴 설명은 표준 텍스트보다 더 모호하다는 것을 알게 될 것입니다.)
를 들어, 「」입니다.x = x + 5
수 에 정답입니다.x + 5
사이에x
단,하지만a[i] = i++
'이러다'가 읽혔기 때문입니다.i
에 있는 은 새로운 .i
( ( ( )의 두 입니다.i
별도로 고려됩니다).
지금 코드로 돌아가세요.는 그것이 잘된 행동이라고 한다. 왜냐하면 것은 ...이기 이다.의 독서가a[0]
어레이 인덱스가 쓰기 전에 확실하게 발생하는지 확인하기 위해 사용합니다.
을 사용하다 이 글을 다 읽고 을 쓸 수 요.a[0]
UB를 사용하다
어떤 분이 시퀀스 포인트에 대해서 댓글을 달아주셨어요.C99에서는 이 식에 시퀀스 포인트가 없기 때문에 시퀀스 포인트는 이 설명에 포함되지 않습니다.
이 C99 코드는 정의되지 않은 동작을 생성합니까?
아니요. 정의되지 않은 동작을 생성하지 않습니다. a[0]
두 시퀀스 포인트 사이에서 한 번만 수정됩니다(첫 번째 시퀀스 포인트는 이니셜라이저의 끝에 있음).int a[3] = {0, 0, 0};
두 뒤에 요.a[a[0]] = 1
를 참조해 주세요.
수정할 개체 자체를 결정하기 위해 개체를 읽는 것은 언급하지 않습니다.따라서 이 문장은 정의되지 않은 동작을 발생시킬 수 있습니다.
오브젝트는 그 자체를 수정하기 위해 여러 번 읽을 수 있으며 이는 완전히 정의된 동작입니다.이 예를 보세요.
int x = 10;
x = x*x + 2*x + x%5;
견적서의 두 번째 문장은 다음과 같습니다.
또한 이전 값은 저장할 값을 결정하기 위해서만 읽어야 한다.
★★★★★★★★★★★★★★★★★.x
사물의 합니다.x
그 자체입니다.
메모: 질문에서 언급된 견적에는 두 가지 부분이 있습니다.첫 번째 부분은 다음과 같습니다.이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 객체는 식을 평가하여 저장된 값을 한 번만 수정해야 합니다.
때문에 '아예'와 같은 입니다.
i = i++;
UB(이전 시퀀스 포인트와 다음 시퀀스 포인트 간에 두 가지 수정)에 해당됩니다.
두 번째 부분은 다음과 같습니다.또한 이전 값은 저장할 값을 결정하기 위해서만 읽어야 합니다. 따라서 다음과 같은 표현은 다음과 같습니다.
a[i++] = i;
j = (i = 2) + i;
UB를 호출합니다. 모두 " " " 입니다.i
시퀀스 시퀀스 포인트 한되지만 가장 시퀀스 되지 않습니다.i
을 i
.
C11 표준에서는 다음과 같이 변경되었습니다.
6.5 표현:
동일한 스칼라 객체에 대한 다른 부작용 또는 동일한 스칼라 객체의 값을 사용한 값 계산과 관련하여 스칼라 객체에 대한 부작용이 시퀀스되지 않은 경우 동작은 정의되지 않습니다[...]
으로 현상 in in in ina[a[0]] = 1
한 a[0]
의 값 과 같습니다.a[0]
is is is is 、 is 、 is 、 is 、 is 、 ationation is isationation 。a[a[0]]
.
C99는 부속서 C의 모든 시퀀스 포인트를 열거한다.의 끝에 하나 있다
a[a[0]] = 1;
완전한 표현 문장이지만 내부에 시퀀스 포인트가 없기 때문입니다.이 「」라고 하는 만,a[0]
먼저 평가해야 하며, 값이 할당된 어레이 요소를 결정하는 데 사용되는 결과는 시퀀스 규칙에 의해 보장되지 않습니다.「」의 .a[0]
0
,a[0]
는, 2개의 시퀀스 포인트 사이에 읽기와 쓰기의 양쪽 모두를 나타내고 있습니다.읽기는 쓸 값을 결정하기 위한 것이 아닙니다.따라서 C99 6.5/2에서는 식을 평가하는 동작은 정의되어 있지 않지만 실제로는 걱정할 필요가 없다고 생각합니다.
이 점에서 C11이 더 낫다.제6.5조 (1)항은 다음과 같이 말한다.
식은 값의 계산을 지정하거나 객체나 함수를 지정하거나 부작용을 발생시키거나 이들 조합을 수행하는 연산자와 오퍼랜드의 시퀀스입니다.연산자의 피연산자 값 계산은 연산자 결과의 값 계산보다 먼저 시퀀싱됩니다.
특히 두 번째 문장은 C99에 아날로그가 없습니다.당신은 그것으로 충분하다고 생각할지 모르지만, 그렇지 않아요.값 계산에는 적용되지만 값 계산과 관련된 부작용의 순서에 대해서는 언급하지 않습니다.왼쪽 피연산자의 값을 갱신하는 것은 부작용이기 때문에 추가 문장이 직접 적용되지 않습니다.
그러나 할당 연산자의 사양이 필요한 시퀀싱을 제공하므로 C11은 이 점에서 우리에게 적합합니다(C11 6.5.16(3):
[...] 왼쪽 피연산자의 저장된 값을 업데이트할 때의 부작용은 왼쪽 피연산자와 오른쪽 피연산자의 값을 계산한 후에 순서대로 처리됩니다.피연산자에 대한 평가는 순서가 매겨지지 않습니다.
(대조적으로 C99는 왼쪽 오퍼랜드의 저장된 값을 이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에 업데이트한다고만 말합니다).섹션 6.5와 6.5.16을 함께 사용하면 C11은 명확하게 정의된 시퀀스를 제공합니다.이너[]
외부보다 먼저 평가됩니다.[]
저장된 값이 업데이트되기 전에 평가됩니다.이는 C11의 6.5(2) 버전을 충족하기 때문에 C11에서는 식을 평가하는 동작이 정의됩니다.
이 값은 잘 정의되어 있습니다.단,a[0]
유효한 어레이인덱스가 아닌 값이 포함되어 있습니다(즉, 코드의 값이 음수가 아니므로 다음 값을 넘지 않습니다).3
) 코드를 읽기 쉽고 동등한 것으로 변경할 수 있습니다.
index = a[0];
a[index] = 1; /* still UB if index < 0 || index >= 3 */
표현 중에a[a[0]] = 1
it is necessary to evaluate a[0]
first. If a[0]
happens to be zero, then a[0]
will be modified. But there is no way for a compiler (short of not complying with the standard) to change order of evaluations and modify a[0]
before attempting to read its value.
A side effect includes modification of an object1.
The C standard says that behavior is undefined if a side effect on object is unsequenced with a side effect on the same object or a value computation using the value of the same object2.
The object a[0]
in this expression is modified (side effect) and it's value (value computation) is used to determine the index. It would seem this expression yields undefined behavior:
a[a[0]] = 1
However the text in assignment operators in the standard, explains that the value computation of both left and right operands of the operator =
, is sequenced before the left operand is modified3.
The behavior is thus defined, as the first rule1 isn't violated, because the modification (side effect) is sequenced after the value computation of the same object.
1 (Quoted from ISO/IEC 9899:201x 5.1.2.3 Program Exectution 2):
Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
2 (Quoted from ISO/IEC 9899:201x 6.5 Expressions 2):
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
3 (Quoted from ISO/IEC 9899:201x 6.5.16 Assignment operators 3):
The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.
ReferenceURL : https://stackoverflow.com/questions/32152371/does-aa0-1-produce-undefined-behavior
'programing' 카테고리의 다른 글
V-inside v-for - 두 열에 항목 목록 표시 (0) | 2022.07.11 |
---|---|
@Nullable 및 @Nonnull 주석을 더 효과적으로 사용하는 방법은 무엇입니까? (0) | 2022.07.11 |
VueX가 변환이 실행되기를 기다립니다. (0) | 2022.07.11 |
vue3의 VueApexCharts 라이브러리에서 데이터와 함께 어레이를 차트에 올바르게 전달하는 방법 (0) | 2022.07.11 |
대문자화된 Vue.js 컴포넌트 (0) | 2022.07.11 |