Nice programing

a [a [0]] = 1은 정의되지 않은 동작을 생성합니까?

nicepro 2021. 1. 7. 21:17
반응형

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;
}

a[a[0]] = 1;에서는 a[0]읽고 수정됩니다.

ISO / IEC 9899의 n1124 초안을 살펴 보았습니다. (6.5 표현식에서) 다음과 같이 말합니다.

이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 객체는 표현식 평가에 의해 최대 한 번 수정 된 저장된 값을 가져야합니다. 또한 이전 값은 저장 될 값을 결정하기 위해서만 읽어야합니다.

수정할 객체 자체를 결정하기 위해 객체를 읽는 것은 언급하지 않습니다. 따라서이 명령문은 정의되지 않은 동작을 생성 할 수 있습니다.

하지만 이상하게 느껴집니다. 이것이 실제로 정의되지 않은 동작을 생성합니까?

(다른 ISO C 버전에서도이 문제에 대해 알고 싶습니다.)


이전 값은 저장 될 값을 결정하기 위해서만 읽혀 져야합니다.

이것은 약간 모호하고 혼란을 일으켰습니다. 이것이 부분적으로 C11이 그것을 버리고 새로운 시퀀싱 모델을 도입 한 이유입니다.

그것이 말하고자하는 것은 : 이전 값을 읽는 것이 새 값을 쓰는 것보다 시간에 일찍 발생하는 것이 보장된다면 괜찮습니다. 그렇지 않으면 UB입니다. 물론 새 값이 작성되기 전에 계산되어야합니다.

(물론 내가 방금 쓴 설명은 표준 텍스트보다 더 모호한 사람들이 발견 할 것입니다!)

예를 들어, 먼저 알지 않고 x = x + 5는 운동을 할 수 없기 때문에 정확합니다 . 그러나 에 저장할 새 값을 계산하기 위해 왼쪽 의 읽기 가 필요하지 않기 때문에 잘못된 것 입니다 . (의 두 읽기는 별도로 고려됩니다).x + 5xa[i] = i++iii


지금 코드로 돌아갑니다. 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 expression a[a[0]] = 1, there is only one side effect to a[0] and the value computation of index a[0] is sequenced before the value computation of a[a[0]].


C99 presents an enumeration of all the sequence points in annex C. There is one at the end of

a[a[0]] = 1;

because it is a complete expression statement, but there are no sequence points inside. Although logic dictates that the subexpression a[0] must be evaluated first, and the result used to determine to which array element the value is assigned, the sequencing rules do not ensure it. When the initial value of a[0] is 0, a[0] is both read and written between two sequence points, and the read is not for the purpose of determining what value to write. Per C99 6.5/2, the behavior of evaluating the expression is therefore undefined, but in practice I don't think you need to worry about it.

C11 is better in this regard. Section 6.5, paragraph (1) says

An expression is a sequence of operators and operands that specifies computation of a value, or that designates an object or a function, or that generates side effects, or that performs a combination thereof. The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

Note in particular the second sentence, which has no analogue in C99. You might think that would be sufficient, but it isn't. It applies to the value computations, but it says nothing about the sequencing of side effects relative to the value computations. Updating the value of the left operand is a side effect, so that extra sentence does not directly apply.

C11 nevertheless comes through for us on this one, as the specifications for the assignment operators provide the needed sequencing (C11 6.5.16(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.

(In contrast, C99 just says that updating the stored value of the left operand happens between the previous and next sequence points.) With sections 6.5 and 6.5.16 together, then, C11 gives a well-defined sequence: the inner [] is evaluated before the outer [], which is evaluated before the stored value is updated. This satisfies C11's version of 6.5(2), so in C11, the behavior of evaluating the expression is defined.


The value is well defined, unless a[0] contains a value that is not a valid array index (i.e. in your code is not negative and does not exceed 3). You could change the code to the more readable and equivalent

 index = a[0];
 a[index] = 1;    /* still UB if index < 0 || index >= 3 */

In the expression 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

반응형