C ++ 'for'루프에 두 개의 증분 문을 어떻게 넣습니까?


93

for하나가 아닌 루프 조건 에서 두 개의 변수를 증가시키고 싶습니다 .

그래서 다음과 같습니다.

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

이것에 대한 구문은 무엇입니까?

답변:


154

일반적인 관용구는 두 피연산자를 모두 평가하고 두 번째 피연산자를 반환하는 쉼표 연산자 를 사용하는 것 입니다. 그러므로:

for(int i = 0; i != 5; ++i,++j) 
    do_something(i,j);

하지만 정말 쉼표 연산자입니까?

이제 그 글을 쓴 어떤 주석가는 그것이 실제로 쉼표 연산자가 아니라 for 문에있는 특별한 구문 설탕이라고 제안했습니다. GCC에서 다음과 같이 확인했습니다.

int i=0;
int a=5;
int x=0;

for(i; i<5; x=i++,a++){
    printf("i=%d a=%d x=%d\n",i,a,x);
}

x가 a의 원래 값을 가져 오기를 기대했기 때문에 x에 대해 5,6,7 ..이 표시되어야합니다. 내가 얻은 것은

i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3

그러나 구문 분석기가 실제로 쉼표 연산자를 보도록하기 위해 표현식을 괄호로 묶으면

int main(){
    int i=0;
    int a=5;
    int x=0;

    for(i=0; i<5; x=(i++,a++)){
        printf("i=%d a=%d x=%d\n",i,a,x);
    }
}

i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8

처음에는 이것이 쉼표 연산자로 작동하지 않는다는 것을 보여 주었다고 생각했지만, 이것은 단순히 우선 순위 문제입니다. 쉼표 연산자는 가능한 가장 낮은 우선 순위 를 가지므로 x = i ++, a ++ 표현식은 효과적으로 (x = i ++), a ++로 구문 분석 됨

모든 의견을 보내 주셔서 감사합니다. 흥미로운 학습 경험이었고 저는 수년 동안 C를 사용해 왔습니다!


1
I for 루프의 첫 번째 또는 세 번째 부분의 쉼표 것을 수회 읽은 하지 콤마 연산자 있지만 단지 컴마. (그러나 C ++ 언어 표준을 파싱하는 데 특히 나쁘기 때문에 이에 대한 공식 소스를 찾지 못했습니다.)
Daniel Daranas

처음에는 당신이 틀렸다고 생각했지만 테스트 코드를 작성했고 당신이 맞습니다. 쉼표 연산자처럼 동작하지 않습니다. 내 대답을 수정할 것입니다!
Paul Dixon

19
그것은 이 문맥에서 콤마 연산자. 예상 한 결과를 얻지 못하는 이유는 명령 연산자가 할당 연산자보다 우선 순위가 낮기 때문에 괄호가 없으면 (x = i ++), j ++로 구문 분석됩니다.
caf

6
쉼표 연산자입니다. 할당은 쉼표 연산자보다 더 강력하게 연결되므로 x = i ++, a ++는 구문 분석되고 (x = i ++), a ++는 x = (i ++, a ++)가 아닙니다. 이 특성은 일부 라이브러리에서 잘못 사용되어 v = 1,2,3; 직관적 인 작업을 수행하지만 v = 1은 오버로드 된 쉼표 연산자가 추가를 수행하는 프록시 객체를 반환하기 때문입니다.
AProgrammer

3
확인. 에서 open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf의 섹션 6.5.3 마지막 부분은 "표현"입니다. (1.6 # 2는 "표현식 목록"을 "쉼표로 구분 된 표현식 목록"으로 정의하지만,이 구조는 6.5.3에 나타나지 않습니다.) 즉, "++ i, ++ j"를 작성할 때 자체적으로 표현식 이어야 하므로 "," 는 쉼표 연산자 (5.18) 여야합니다 . (이것은 "이니셜 라이저 목록"이나 "함수에 대한 인수 목록"이 아닙니다. 5.18 # 2에서 말한 것처럼 "쉼표에 특별한 의미가 부여 된"예입니다.) 그래도 약간 혼란 스럽습니다.
Daniel Daranas

56

이 시도

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);

18
+1 첫 번째 부분에서 j를 선언 할 수도 있습니다. for (int i = 0, j = 0; i! = 5; ++ i, ++ j) {...}
Daniel Daranas 2009-08-05

1
+1 참고로이 동일한 구문은 C #에서 작동합니다 (Google 검색에서 "C # for loop incrament 2 counters"를 검색하여이 부분을 언급 할 것이라고 생각했습니다).
CodingWithSpike

@CodingWithSpike : 음, C #에서 쉼표 특별합니다. 실제로 쉼표 연산자 표현식이 거기에 나타나는 것은 합법적이지 않습니다. C ++에서 쉼표 연산자를 합법적으로 사용했지만 C #에서 거부 한 예 :for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigt는 쉼표와 관련이 없습니다. 이것은 합법적 인 C #도 아닙니다 for(int i = 0; i != 5; (++i)) {. 컴파일러가 더 이상 "증가"연산이 아니라고 생각하도록하는 추가 괄호 속임수입니다.
CodingWithSpike

@CodingWithSpike : 사실이지만 괄호 C #에서 쉼표를 보는 방식 변경하고 for 작업 내부의 특별한 의미를 방지합니다.
Ben Voigt

6

하지 마십시오!

에서 http://www.research.att.com/~bs/JSF-AV-rules.pdf :

AV 규칙 199
for 루프의 증분 표현식은 단일 루프 매개 변수를 루프의 다음 값으로 변경하는 것 외에 다른 작업을 수행하지 않습니다.

근거 : 가독성.


4
그것은 사실이지만 공정하게 말하자면, 나는 정원 다양성 C (++) 프로그램이 아닌 전투기에 내장 된 소프트웨어에 대한 규칙의 표준이 작성되었다고 확신합니다. 그렇긴해도 F-35 소프트웨어를 디자인하게 될 것이고 깨는 습관이 줄어들 것입니다.
galois


2

저는 FOR 루프의 증분 절에 두 번째 인덱스를 코딩하는 방법을 상기하기 위해 여기에 왔습니다. 주로 C ++로 작성된 다른 프로젝트에 통합 한 샘플에서이를 관찰하여 수행 할 수 있다는 것을 알고있었습니다.

오늘 저는 C #으로 작업하고 있지만 FOR 문은 모든 프로그래밍에서 가장 오래된 제어 구조 중 하나이기 때문에 이와 관련하여 동일한 규칙을 따를 것이라고 확신했습니다. 고맙게도 저는 최근에 이전 C 프로그램 중 하나에서 FOR 루프의 동작을 정확하게 문서화하는 데 며칠을 보냈으며 그 연구에서 오늘날의 C # 문제, 특히 두 번째 인덱스 변수의 동작에 적용되는 교훈이 있다는 것을 금방 깨달았습니다. .

다음은 내 관찰에 대한 요약입니다. 오늘 내가 본 모든 일은 Locals 창에서 변수를주의 깊게 관찰함으로써 C # FOR 문이 C 또는 C ++ FOR 문과 똑같이 작동한다는 내 기대를 확인했습니다.

  1. FOR 루프가 처음 실행될 때 증분 절 (3 개 중 3 번째 절)을 건너 뜁니다. Visual C 및 C ++에서 증분은 루프를 구현하는 블록 중간에 3 개의 기계 명령어로 생성되므로 초기 단계에서 초기화 코드를 한 번만 실행 한 다음 증분 블록을 건너 뛰어 종료 테스트를 실행합니다. 이것은 인덱스 및 제한 변수의 상태에 따라 FOR 루프가 0 회 이상 실행되는 기능을 구현합니다.
  2. 루프 본문이 실행되는 경우 마지막 문은 첫 번째 반복에서 건너 뛴 세 개의 증가 명령 중 첫 번째로 점프하는 것입니다. 이러한 실행 후 제어는 중간 절을 구현하는 제한 테스트 코드로 자연스럽게 떨어집니다. 해당 테스트의 결과는 FOR 루프의 본문이 실행되는지 또는 제어가 해당 범위의 맨 아래에있는 점프를지나 다음 명령어로 전송되는지 여부를 결정합니다.
  3. 제어는 FOR 루프 블록의 맨 아래에서 증가 블록으로 전송되기 때문에 테스트가 실행되기 전에 인덱스 변수가 증가합니다. 이 동작은 학습 한 방식으로 제한 절을 코딩해야하는 이유를 설명 할뿐만 아니라 쉼표 연산자를 통해 추가하는 보조 증분에 영향을줍니다. 이는 세 번째 절의 일부가되기 때문입니다. 따라서 첫 번째 반복에서 변경되지 않고 본문을 실행하지 않는 마지막 반복에서 변경됩니다.

루프가 끝날 때 인덱스 변수 중 하나가 범위에 남아 있으면 해당 값은 실제 인덱스 변수의 경우 루프를 중지하는 임계 값보다 하나 더 높습니다. 마찬가지로, 예를 들어 두 번째 변수가 루프에 들어가기 전에 0으로 초기화되면 마지막 값은 감소가 아닌 증가 (++)이고 루프의 본문은 값을 변경합니다.


1

나는 squelart에 동의합니다. 두 변수를 증가시키는 것은 버그가 발생하기 쉬우 며 특히 그중 하나만 테스트하는 경우 더욱 그렇습니다.

다음은이를 수행하는 읽기 쉬운 방법입니다.

int j = 0;
for(int i = 0; i < 5; ++i) {
    do_something(i, j);
    ++j;
}

For루프는 하나의 증가 / 감소 변수에서 루프가 실행되는 경우를 의미합니다. 다른 변수의 경우 루프에서 변경하십시오.

j연결 해야하는 경우 i원래 변수를 그대로두고 추가하지 않는 이유는 i무엇입니까?

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

논리가 더 복잡한 경우 (예를 들어 실제로 둘 이상의 변수를 모니터링해야 함) while루프를 사용합니다 .


2
첫 번째 예에서 j는 i보다 한 번 더 증가합니다! 첫 x 단계에 대해 몇 가지 작업을 수행해야하는 반복기는 어떻습니까? (그리고 컬렉션은 항상 충분히 길다) 반복 할 때마다 반복자를 올릴 수 있지만 훨씬 더 깔끔한 imho입니다.
Peter Smit


0

수학을 사용하십시오. 두 연산이 수학적으로 루프 반복에 의존한다면 왜 수학을하지 않습니까?

int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
    do_something (counter+i, counter+j);

또는 더 구체적으로 OP의 예를 참조하십시오.

for(int i = 0; i != 5; ++i)
    do_something(i, j+i);

특히 값으로 함수에 전달하는 경우 원하는 것을 정확히 수행하는 무언가를 얻어야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.