"++i"와 "i++"은 어떻게 다른가요?

Twibap의 이미지

안녕하세요.

저는 ++i의 경우에 i값을 사용하기 전에 1 증가시킨 뒤 (증가한)i값을 사용하고, i++의 경우는 i 값을 사용 한 뒤 i값을 1 증가시키는 것으로 알고있습니다.

Stroustrup의 4장 시험해보기를 풀다가 의문이 생겼습니다.

// a부터 z까지 각 소문자에 해당하는 정수값 출력하기.
#include "std_lib_facilities.h"
char i = 'a';
while (i {
cout ++i; // (2)
}

여기서 (2)번을 없애기 위해 (1)을 이렇게 수정했습니다.

char i = 'a';
while (i {
cout }

전자의 경우는 a부터 z까지 소문자와 정수를 제대로 출력하지만 후자의 경우는 b부터 z를 지나 '{' 까지 출력하면서 프로그램이 종료됩니다.

제가 알고있는것이 정확하다면 후자도 전자와 같은 결과를 출력해야하는데 그렇지 않네요.
어디가 문제인지 해설 부탁드리겠습니다 ㅠ.ㅠ

shint의 이미지

http://babytiger.tistory.com/73

대입문에 i++나 --i를 사용해서 어려움을 겪게 될 수 있으니.
별도의 줄에 적어서 ++i등을 사용하는게 편합니다.

//헛갈릴 수 있슴.
x = i++;
 
//보기 편함
x = i;
i++;

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

Twibap의 이미지

코드가 적을수록 가독성이 좋을것이라 생각했었는데 오산이었군요. 감사합니다.

ifree의 이미지

표현에 대한 가독성 여부는 자기 기준이죠.
스스로 = ++i += 같은 표현이 익숙하고 확실히 알고 있다면 그대로 쓰셔도 되요.

jick의 이미지

하나의 문장에서 ++i와 i를 섞어 쓰는 것은 정의되지 않은 행동입니다. 컴파일러 마음에 따라 아무 값이나 나올 수 있습니다.

마찬가지로 a << b << c 같은 수식이 있을 때 a, b, c 중 어느 값을 먼저 계산하느냐는 역시 컴파일러 마음입니다. (이부분 헷갈리시는 분들이 종종 계신데, 괄호로 아무리 싸주어도 소용없습니다. 괄호는 수식이 어떻게 묶이느냐를 정해주는 것이지 어느 부분을 먼저 계산하느냐를 정해주지 않습니다.)

hsnks100의 이미지

말씀하신 부분은 살짝 틀린 부분이 있습니다.

++i 와 i 를 동시에 쓸 수 있고, 모호하지도 않습니다.

표준 문서를 인용해보면
Except where noted, the order of evaluation of operands of individua operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

하나의 expression 에 side-effect 가 발생할 수 있는 경우에 스펙이 없는겁니다.

해당 문제는 적법합니다.

----------------------------------------------------
개인 블로그: https://kangssu.com

qiiiiiiiip의 이미지

side effects가 발생하는 order 가 unspecified되었다는 뜻이겠지요..

--

그전에 ++i 가 섞여있는 expression이 side effect가 없을 수도 없고요..

익명 사용자의 이미지

시퀸스 포인트 이전에는 ++i에 의해 i의 값이 실제로 증가하는 시점이 정해져 있지 않기 때문에, 당연히 문제가 생깁니다.

그리고 해당 구절을 왜 인용하셨는지 모르겠는데... 사이드 이펙트 적용 순서가 unspecified이므로, 이에 의존하는 코딩은 예측할 수 없는 결과를 낳습니다.(implementation-defined된 경우하고 다릅니다.)

표준 문서를 가지고 계시다면 표준 문서 내에 unspecified가 무슨 뜻인지 따로 설명해 놓은 부분이 있으므로, 일반적인 의미로 해석하지 마시고 꼭 표준문서 해당 부분 찾아보시길 바랍니다.

oosap의 이미지

전위증가와 후위증가의 기대되는 결과가 동일하다면,

성능을 위해서는 C++ 에서는 전위증가를 추천합니다.

그러니까

for ( int i ; i < 100 ; i++ )

보다는

for ( int i ; i < 100 ; ++i )

가 성능에서 훨씬 큰 이득을 줄 수도 있습니다.

이유는 연산자 오버로딩의 구현시에 후위증가연산자의 구현체가 실행될 때는 임시객체의 생성과 소멸이 매번 동반될 수 있기 때문입니다. 위 예시코드에서는 이해를 간단히 하기위해 언어에서 정의한 기본 타입을 사용했지만 그렇지 않고 사용자 정의 타입을 사용하는 경우 혹은 이터레이터인 경우 그 차이가 클 것입니다.
출처는 윤성우선생님의 C++ 책 되겠습니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

ifree의 이미지

cout << i <<'\t'<< int(i++) 에서
cout, i, '\t', int(i++) 이 모두 오퍼레이러 << 의 인수입니다.
합성함수와 비슷한 형태로서 가장 뒤 오퍼레이러(합성함수의 가장 안쪽)의 인수부터 evaluate되기 때문에 말씀하신 결과가 나오게 됩니다.
cout << int(i++) <<'\t'<< i 로 해보세요.

Twibap의 이미지

그러나 컴파일러에 따라서 결과가 다르게 나오는것을 보니 결국 컴파일러 성향 문제인것 같네요.

Visual studio Express 2012에서는 "cout << int(i++) <<'\t'<< i"과 "cout << i <<'\t'<< int(i++)"의 결과가 순서를 제외하고 같습니다.

xCode에서는 "cout << i <<'\t'<< int(i++)"를 쓰더라도 맨 처음 원했던 결과가 나오더라구요

DarkSide의 이미지

<< 처리 방식이 컴파일러마다 다른가요?
gcc 를 표준으로 본다면 원하시는 결과를 위해 cout << int(i++) <<'\t'<< i" 를 쓰시는게 맞습니다만.

jick의 이미지

(제가 아는 한에서는) << 연산자는 연산 순서를 지정해주지 않습니다. 예를 들면 다음과 같은 문장이 있을 때
cout << f() << g() << h()

f, g, h 중 어느 함수가 먼저 불릴지는 컴파일러 마음입니다.

===
http://en.cppreference.com/w/cpp/language/eval_order

2) If a side effect on a scalar object is unsequenced relative to a value computation using the value of the same scalar object, the behavior is undefined.

cout << i << i++; // undefined behavior
a[i] = i++; // undefined bevahior

oosap의 이미지

'<<' 연산자는 연산 순서를 지정해주지 않습니다.

라고 하셨는데요,

오버로딩된 연산자는 의미를 바꿔도 우선 순위나 피연산자의
개수 등은 원래 연산자의 것을 그대로 사용한다는 점을 주의해야 한다.

http://www.soenlab.com/lecture/ccpp/cpp4/36-1-2.htm

연산자의 우선순위는 오버로딩에 의해 영향받지않는다.

https://ko.wikipedia.org/wiki/C%EC%99%80_C%2B%2B%EC%97%90%EC%84%9C%EC%9D%98_%EC%97%B0%EC%82%B0%EC%9E%90#.EC.97.B0.EC.82.B0.EC.9E.90_.EC.9A.B0.EC.84.A0.EC.88.9C.EC.9C.84

라고 하는 자료들이 있습니다.
이 자료들 중 첫번째 자료는 cout 이 오버로딩한 '삽입연산자'를 설명하는데 쉬프트연산자의 우선순위값 '7'은 오버로딩이 되더라도 유지가 된다는 의미로 읽었습니다.

cout << f() << g() << h()

에서 f, g, h 중 어느 함수가 먼저 불릴지는 연산자 우선순위 규칙에 따라서 아래와 같이 평가되지 않을까요?

((cout << f()) << g()) << h()

이들에 따르면 jick 님이 잘못 알고 계신 것 같아 몇줄 적어봅니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

익명 사용자의 이미지

제가 C++에 문외한이라 확인된 내용은 아닌데...

이항 연산자 . 와 ->에 대해서, 피연산자들의 평가순서가 아마 정해져 있지 않을 겁니다.
(instA).(funcA)(a,b,c);
위와 같은 함수 호출식이 있을때, instA먼저 평가할지, funcA먼저 평가할지가 불분명할 겁니다.

오버로딩된 <<에 대해서도 연결 방식은 정해져 있을 지언정, 어느쪽을 먼저 평가할지는 아마 정해져 있지 않을 겁니다. unspecified이건 implementation-defined이건... 제가 C++ 표준문서가 없어서 뭐라고 단언하긴 뭐합니다만... 이건 누구 가지신 분이 좀 확인해 주셨으면 좋겠고요...

((cout << f()) << g()) << h()

연결은 이렇게 하는 것이 맞겠지만, ((cout << f()) << g())를 먼저 평가할지 h()를 먼저 평가할지는 알 수 없을 겁니다. 괄호 안의 수식에 대해서도 (cout << f())를 먼저 평가할지 g()를 먼저 평가할지가 불분명 하겠지요.

oosap의 이미지

.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

oosap의 이미지

연산자 우선순위 규칙에 따라서 아래와 같이 평가되지 않을까요?
((cout << f()) << g()) << h()
라고 제가 이야기 했었는데, "C++ Standard Library 튜토리얼 레퍼런스 - 조슈티스(1판, C++11 아닙니다.)" 674 페이지에 이런 글이 있습니다.
<< 연산자는 왼쪽으로부터 오른쪽으로 평가된다.
이 책에서도 f,g,h 함수와 비슷한 상황의 설명이 등장합니다. 연산자의 우선순위표에는 우선순위뿐만 아니라 결합법칙의 방향(-->, <--)도 나와있습니다. 같은 우선순의의 연산자들간에 경합이 되는 경우 어떤 방향으로 식을 평가할지를 결정하는 것 같습니다. 이를 짚어 생각해보니 제가 한 말을 수정해야겠다는 생각이 듧니다.
연산자의 '결합법칙'의 규칙에 따라서 아래와 같이 평가되지 않을까요?
((cout << f()) << g()) << h()

여러가지 근거자료들을 찾아보았지만 표준 문서는 안읽어보았습니다. 표준문서를 몇번 보았지만 읽어도 무슨 말인지 모르겠더군요...
위에 익명님 이야기를 읽고 제가 좀 헷갈렸습니다만 이해가 잘 되지 않아 답변하기 좀 어려웠습니다.
제가 서명에 썼듯이 제 의견일 뿐입니다. 물론 틀렸을 가능성이 크며 저는 제 의견에 절대 책임지지 않습니다. ^^;

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

익명 사용자의 이미지

((cout << f()) << g()) << h() 는 맞습니다.
하지만 이는 "의미"를 정해줄 뿐이지 여기에 등장하는 세 expression f(), g(), h() 중 어떤 것이 "런타임에 먼저 계산될 지"를 정해주지는 않습니다.
이미 여러 분이 말씀하셨지만 이 순서는 정해져 있지 않습니다.

공간적인 순서와 시간적인 순서를 헷갈리시면 안되고, 표현의 결합 순서와 런타임에 실행되는 순서를 헷갈리시면 안됩니다.

함수로 표현을 바꿔보면 더 쉽게 눈에 보일 겁니다. <<를 write라고 쓰도록 합시다. 그러면
write(write(write(cout, g()), h())
이 됩니다. 가장 바깥쪽 write의 두 인자인 write(write(cout, g()) 와 h() 중 어느 것이 런타임에 먼저 평가될 지가 정해져있지 않다는 것은 아실겁니다.
그럼 f(), g(), h() 중 어느 것이 먼저 평가될지 정해져있지 않다는 것이 바로 이해되지 않나요?

역시 다른 분들이 이미 말씀하셨지만,
side effect가 없다면 전혀 신경 쓸 필요가 없는 문제입니다. 그러면 "순서"는 전혀 중요하지 않으니까요.
그래서 side effect 없거나 잘 제한되어 있는 구현이 중요한겁니다.
명확한 표현에만 신경쓰면 되고, 실행 순서를 컴파일러가 좋을대로 바꿀 수 있으니까 최적화에도 유리하지요.

익명 사용자의 이미지

잘못 썼네요.

write(write(write(cout, g()), h()) 는 write(write(write(cout, f()), g()), h()) 로 고치고

"두 인자인 write(write(cout, g()) 와 h() 중" 은 "두 인자인 write(write(cout, f()), g()) 와 h() 중" 으로 고칩니다.

oosap의 이미지

There is no concept of left-to-right or right-to-left evaluation in C, which is not to be confused with left-to-right and right-to-left associativity of operators: the expression a + b + c is parsed as (a + b) + c due to left-to-right associativity of operator+, but the subexpression c may be evaluated first (or last, or at the same time as a or b) at run time.

http://en.cppreference.com/w/c/language/eval_order

jick 님이 올려주신 링크에서 찾은 내용입니다. 제가 잘못 이해하고 있던 내용을 짚어주고 있네요..
익명님의 이야기도 이제 좀 알 것 같습니다.
잘못알고 있던 걸 깨우칠 수 있었습니다. 감사합니다.
결국 jick 님과 익명님이 맞고 제가 틀렸습니다. 혹시 다른 분들이 혼란하실까 확인해봅니다.

그리고 이와 관련되서 좀 더 찾아보았습니다. 제가 인용한 위 문장만 보면 궁금증이 생겼었습니다.

if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1"))

오픈소스 컴파일러 clang 의 main() 함수에 등장하는 코드입니다. 이와 같이 if 문에서 && 연산자를 쓸 때는 left-to-right evaluation 의 순서가 적용되는 걸 볼 수 있습니다. 이게 위에서 말한

There is no concept of left-to-right or right-to-left evaluation in C
와 배치되는 것 처럼 보였거든요.

http://en.cppreference.com/w/c/language/operator_logical
여기에서 보니 논리연산자는 '숏서킷' evaluation 이라는 규칙이 적용되고 있습니다.
http://en.cppreference.com/w/c/language/eval_order
여기에서도 하위에 Rules 에 보면, 아래와 같이 얘기하고 있습니다.

3) There is a sequence point after evaluation of the first (left) operand and before evaluation of the second (right) operand of the following binary operators: && (logical AND), || (logical OR), and , (comma).
4) There is a sequence point after evaluation of the first (left) operand and before evaluation of the second or third operand (whichever is evaluated) of the conditional operator ?:

결국 논리연산자 '&&', '||', 콤마연산자 ',' 그리고 삼항연산자 '?:' 와 같은 경우에는 Evaluation Order 가 있다고 볼 수 있는 것 같습니다.

혼자 궁금해해서 좀 더찾아본 내용이라 올려둡니다. 스택오버플로에서도 관련 문답이 있었습니다.
http://stackoverflow.com/questions/7618590/order-of-evaluation-of-expression

시퀀스포인트, 사이드이펙트, 오퍼레이터 아규먼트 등 이런 개념이 설명된 C 혹은 C++ 문법책이 있는지 궁금합니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

Twibap의 이미지

원래 의도한 코드는 이것입니다.

// a부터 z까지 각 소문자에 해당하는 정수값 출력하기.
#include "std_lib_facilities.h"
char i = 'a';
while (i<='z')
{
	cout << i &lt;< '\t' &lt;< int(i) &lt;< endl;	// (1)
	++i;						// (2)
}

이것에서 2번을 없애기 위해

while (i<='z')
{
	cout << i <<'\t'<< int(i++) << endl;	// (1)
}

이렇게 바꾸었던것입니다.
본문이 조금 망가져있지만 다들 제 의도를 파악하시고 답글을 달아주셨네요.
기대한것 이상의 정보를 얻어갑니다. 감사합니다!

ps. 여기서도 코드가 망가지네요;;

익명 사용자의 이미지

하나는 template<typename T> T operator++()
다른 건 template<typename T> T operator++(int)
입니다.

이게 차이점이죠. ^^

ryutuna의 이미지

냉무.

--------------------------------------------------------------
세상엔 알아야 할 것도 알지 말았어야 할 것도 너무 많았습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.