정의되지 않은 동작 및 시퀀스 포인트가 다시로드 됨


84

이 주제를 다음 주제의 속편으로 간주하십시오.

이전 기사
정의되지 않은 동작 및 시퀀스 포인트

재미 있고 복잡한 표현을 다시 살펴 보겠습니다 (이탤릭체 표현은 위의 주제 * smile *에서 가져온 것입니다).

i += ++i;

우리는 이것이 정의되지 않은 행동을 유발한다고 말합니다. 나는 이것을 말할 때 type of i가 내장 유형 중 하나 라고 암시 적으로 가정한다고 가정합니다 .

유형i사용자 정의 유형 이면 어떻게 됩니까? 유형 Index이이 게시물의 뒷부분에 정의되어 있다고 말합니다 (아래 참조). 여전히 정의되지 않은 동작을 호출합니까?

그렇다면 그 이유는 무엇입니까? 글을 쓰는 것과 같지 i.operator+=(i.operator++());않거나 구문 적으로 더 간단하지 i.add(i.inc());않습니까? 아니면 그들도 정의되지 않은 동작을 호출합니까?

아니라면 왜 안됩니까? 결국, 객체 i는 연속 된 시퀀스 포인트 사이에서 두 번 수정 됩니다 . 경험상의 규칙을 기억하십시오. 표현식은 연속 된 "시퀀스 포인트 사이에서 객체의 값을 한 번만 수정할 수 있습니다 . i += ++i표현식 인 경우 정의되지 않은 동작을 호출해야합니다. 그렇다면 해당 항목 i.operator+=(i.operator++());i.add(i.inc());정의되지 않은 동작을 호출해야합니다. 사실이 아닌 것 같습니다! (내가 이해하는 한)

또는, i += ++i아닌 표현 으로 시작하려면? 그렇다면 그것은 무엇이며 표현 의 정의는 무엇 입니까?

식이고 동시에 동작 잘 정의되어 있으면 식과 관련된 시퀀스 포인트의 수가 식에 포함 된 피연산자 의 유형 에 따라 달라진다는 것을 의미합니다 . 내가 맞습니까 (일부라도)?


그런데이 표현은 어떻습니까?

//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!

a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.

당신은 당신의 응답에서도 이것을 고려해야합니다 (당신이 그 행동을 확실히 알고 있다면). :-)


이다

++++++i;

C ++ 03에서 잘 정의되어 있습니까? 결국 이것은 이것입니다.

((i.operator++()).operator++()).operator++();

class Index
{
    int state;

    public:
        Index(int s) : state(s) {}
        Index& operator++()
        {
            state++;
            return *this;
        }
        Index& operator+=(const Index & index)
        {
            state+= index.state;
            return *this;
        }
        operator int()
        {
            return state;
        }
        Index & add(const Index & index)
        {
            state += index.state;
            return *this;
        }
        Index & inc()
        {
            state++;
            return *this;
        }
};

13
멋진 답변에 영감을 준 멋진 질문이 1 개 있습니다. 나는 그것을 더 읽을 수 리팩토링해야 끔찍한 코드는 아직 말할한다고 느끼지만, 당신은 아마 어쨌든 :) 알
필립 포터

4
@ 질문은 무엇입니까 : 누가 동일하다고 말했습니까? 아니면 누가 같지 않다고 했습니까? 구현 방법에 의존하지 않습니까? (참고 : 나는의 유형을 믿고있어 s입니다 사용자 정의 유형!)
나와 즈

5
두 시퀀스 포인트 사이에서 두 번 수정되는 스칼라 객체가 보이지 않습니다 ...
Johannes Schaub-litb

3
@Johannes : 스칼라 객체 에 관한 것입니다. 뭐야? 왜 전에 들어 본 적이 없는지 궁금합니다. 어쩌면 튜토리얼 / C ++-faq에서 언급하지 않았거나 강조하지 않았기 때문일까요? 내장형 객체와 다른가요 ?
Nawaz 2011 년

3
@Phillip : 당연히 실생활에서 그런 코드를 작성하지는 않을 것입니다. 사실, 정상적인 프로그래머는 그것을 작성하지 않을 것입니다. 이러한 질문은 일반적으로 정의되지 않은 동작 및 시퀀스 포인트의 전체 비즈니스를 더 잘 이해할 수 있도록 고안되었습니다! :-)
Nawaz 2011 년

답변:


48

코드처럼 보입니다.

i.operator+=(i.operator ++());

시퀀스 포인트와 관련하여 완벽하게 작동합니다. C ++ ISO 표준의 섹션 1.9.17은 시퀀스 포인트 및 함수 평가에 대해 다음과 같이 말합니다.

함수를 호출 할 때 (함수가 인라인인지 여부에 관계없이) 모든 함수 인수 (있는 경우)의 평가 후에 함수 본문의 표현식 또는 명령문을 실행하기 전에 발생하는 시퀀스 포인트가 있습니다. 또한 반환 된 값을 복사 한 후 함수 외부의 표현식을 실행하기 전에 시퀀스 지점이 있습니다.

예를 들어, i.operator ++()에 매개 변수가 operator +=평가 후 시퀀스 포인트 가 있음을 나타냅니다 . 간단히 말해서 오버로드 된 연산자는 함수이므로 일반적인 순서 지정 규칙이 적용됩니다.

그건 그렇고, 좋은 질문입니다! 내가 이미 알고 있다고 생각했던 언어의 모든 뉘앙스를 이해하도록 강요하는 방식이 정말 마음에 듭니다. :-)



11

다른 사람들이 말했듯이 i += ++i 이 함수를 호출하고 함수가 시퀀스 포인트를 구성하기 때문에 예제는 사용자 정의 유형으로 작동합니다.

반면에 기본 배열 유형 또는 사용자 정의 유형 a[++i] = i이라고 가정하면 운이 좋지 않습니다 a. 여기에있는 문제는 포함하는 표현식의 어느 부분이 i먼저 평가 되는지 알 수 없다는 것입니다 . 그것은 그 수 ++i, 평가에 떨어져 통과 operator[]가 객체를 검색하기 위해 (또는 원시 버전), 다음의 값이 i(i가 증가 된 이후 인)이 전달됩니다. 다른 한편으로, 아마도 후자 측이 먼저 평가되고 나중에 할당을 위해 저장되고 ++i부품이 평가됩니다.


그래서 ... 표현식 평가 순서가 지정되지 않았기 때문에 결과가 UB가 아니라 지정되지 않았습니까?
Philip Potter

@Philip : unspecified는 컴파일러가 동작을 지정할 것으로 예상하는 반면 undefined는 그러한 의무를 부여하지 않음을 의미합니다. 컴파일러에게 최적화를위한 더 많은 공간을 제공하는 것은 여기에 정의되어 있지 않다고 생각합니다.
Matthieu M.

@Noah : 나도 답글을 올렸다. 그것을 확인하고 당신의 생각을 알려주십시오. :-)
Nawaz 2011 년

1
@Philip : 5/4의 규칙 때문에 결과는 UB입니다. "이 단락의 요구 사항은 전체 표현식의 하위 표현식의 허용 가능한 순서마다 충족되어야합니다. 그렇지 않으면 동작이 정의되지 않습니다." 허용되는 모든 순서에 수정 ++ii할당의 RHS 읽기 사이에 시퀀스 포인트가있는 경우 순서가 지정되지 않습니다. 허용 가능한 순서 중 하나가 시퀀스 포인트없이이 두 가지 작업을 수행하기 때문에 동작이 정의되지 않습니다.
Steve Jessop 2011 년

1
@Philip : 지정되지 않은 동작을 정의되지 않은 동작으로 정의한 것이 아닙니다. 또, 경우에 없는 동작 범위가 정의 된 일부를 포함하는, 다음 의 전체 동작이 정의되지 않는다. 경우 지정되지 않은 행동의 범위는 모든 가능성에 정의되어, 다음 전체 동작은 지정되지 않습니다. 그러나 두 번째 요점에 맞습니다. 사용자 정의 a및 내장 i.
Steve Jessop 2011 년

8

잘 정의되어 있다고 생각합니다.

C ++ 초안 표준 (n1905) §1.9 / 16에서 :

"반환 된 값을 복사 한 후 함수 외부의 표현식을 실행하기 전에 시퀀스 지점도 있습니다 13). C ++의 여러 컨텍스트는 해당 함수 호출 구문이 번역 단위에 나타나지 않더라도 함수 호출을 평가합니다. [ : 새로운 표현의 평가는 하나 개 이상의 할당 및 생성자 함수 호출, 5.3.4 참조 다른 예를 들면, 변환 함수 (12.3.2)의 호출 상황에서 발생할 수없는 함수 호출 구문 나타날로 -.. 최종 ] 함수 입력 및 함수 종료 (위에서 설명한대로)의 시퀀스 지점 은 표현식의 구문에 관계없이 평가 된 함수 호출의 기능 입니다. 함수를 호출하는 는 다음과 같습니다. "

내가 굵은 부분을 주목하십시오. 즉, 증가 함수 호출 ( i.operator ++()) 이후 복합 할당 호출 ( i.operator+=) 이전에 실제로 시퀀스 포인트가 있음을 의미 합니다.


6

좋구나. 이전 답변을 살펴본 후 저는 제 자신의 질문에 대해 다시 생각했습니다. 특히이 부분은 노아 만이 대답 하려고 했지만 그에 대해 완전히 확신하지는 못했습니다.

a[++i] = i;

사례 1 :

a내장 유형의 배열 인 경우 . 그러면 노아가 말한 것이 맞습니다. 그건,

a [++ i] = i는 a가 기본 배열 유형이라고 가정하면 운이 좋지 않습니다. 또는 심지어 사용자 정의 . 여기에있는 문제는 i를 포함하는 표현식의 어느 부분이 먼저 평가되는지 알 수 없다는 것입니다.

따라서 a[++i]=i정의되지 않은 동작을 호출하거나 결과가 지정되지 않습니다. 그것이 무엇이든, 그것은 잘 정의되지 않았습니다!

추신 : 위 인용에서, 취소 선 물론 내 것입니다.

사례 2 :

경우 a과부하 사용자 정의 형식의 목적은 operator[], 다시 두 경우가있다.

  1. 오버로드 된 operator[]함수 의 반환 유형 이 내장 유형이면 다시 a[++i]=i정의되지 않은 동작을 호출하거나 결과가 지정되지 않습니다.
  2. 과부하의 반환 형식하지만 operator[]기능의 다음 동작을 사용자 정의 유형이다 a[++i] = i(지금까지의 내가 이해) 잘 정의되어있다가,이 경우 이후 a[++i]=i서면에 해당 a.operator[](++i).operator=(i);,과 동일합니다 a[++i].operator=(i);. 즉, 반환 된 객체 operator=에 대해 할당 이 호출 되는데, 이는 매우 잘 정의 된 것 같습니다. 시간이 반환 될 때 까지 이미 평가되었으므로 반환 된 객체는 의 업데이트 된 값을 인수로 전달하는 함수를 호출 합니다 . 이 두 호출 사이에는 시퀀스 포인트a[++i]a[++i]++ioperator=i 있습니다. 구문은이 두 호출 사이에 경쟁이 없음을 보장합니다.operator[]먼저 호출되고 연속적으로 ++i전달 된 인수 도 먼저 평가됩니다.

someInstance.Fun(++k).Gun(10).Sun(k).Tun();각 연속 함수 호출이 사용자 정의 유형의 객체를 반환하는 것으로 생각하십시오 . 나에게이 상황은 더 비슷해 보인다. eat(++k);drink(10);sleep(k)두 상황 모두에서 각 함수 호출 후에 시퀀스 포인트가 존재하기 때문이다.

내가 틀렸다면 나를 고쳐주세요. :-)


1
@Nawaz k++및이 k되어 있지 시퀀스으로 분리. 둘 중 하나를 먼저 평가 Sun하거나 Fun평가할 수 있습니다. 언어는 단지 그 요구 Fun하기 전에 평가 Sun하는 것이 아니라 Fun의 인수 전에 평가 Sun의 인수 '. 참조를 제공 할 수없는 상태에서 동일한 내용을 다시 설명하고 있으므로 여기서부터 진행하지 않을 것입니다.
Philip Potter

1
@Nawaz : 그것들을 분리하는 시퀀스 포인트를 정의하는 것이 없기 때문입니다. Sun실행 전후에 시퀀스 포인트가 있지만 Fun의 인수 ++k는 그 전후에 평가 될 수 있습니다. Fun실행 전후에 시퀀스 포인트가 있지만 Sun의 인수 k는 그 전후에 평가 될 수 있습니다. 따라서, 한 가지 경우 모두이다 k하고 ++k어느 평가 전 Sun또는 Fun평가되고, 두하도록 함수 호출 시퀀스 점 전에, 그리고 그렇게 분리시키는 순서 점 없다 k하고 ++k.
Philip Potter

1
@Philip : 반복합니다 :이 상황이 어떻게 다른 eat(i++);drink(10);sleep(i);가요? ... 지금도 i++그 전후에 평가 될 수 있다고 말할 수 있습니까?
Nawaz 2011 년

1
@Nawaz : 어떻게하면 더 분명해질 수 있습니까? 펀 / 일 예에서, 존재하지 아니 사이에 순서 지점 k++k . 맛집 / 음료 예에서,이 사이 시퀀스 지점 ii++.
Philip Potter

3
@Philip : 전혀 말이되지 않습니다. Fun ()과 Sun () 사이에는 시퀀스 포인트가 있지만 인수 사이에는 시퀀스 포인트가 없습니다. 그 사이에eat()sleep()존재 시퀀스 포인트 (들), 그러나 거기 사이에 인수는 하나도 없습니다. 시퀀스 포인트로 분리 된 두 함수 호출에 대한 인수가 동일한 시퀀스 포인트에 속할 수있는 방법은 무엇입니까?
Nawaz 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.