증감연산자가 ??

vani2의 이미지

#include
using namespace std;
int main(void) {

int a = 0;

a = ++a + ++a;
cout << a << endl;
return 0;
}

출력값 : 4

예상한 대로라면 3이 나와야 할탠데

왜 4가 나올까요??

ymir의 이미지

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

simminjo의 이미지

컴파일러마다 저런 수식의 경우 연산방식이 다를 수 있습니다.
gcc, vc 각각 다를 수 있습니다.....

---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~

익명 사용자의 이미지

a = ++a+++a;

a = (((++(a++))+a);

우선순위 때문에 4가 되는게 아닐까요?

2 + 2 = 4..

jick의 이미지

많은 분들이 착각하시는데 우선순위는 수식을 해석할 때 어디부터 묶느냐를 결정하는 것이지 그중 어느 부분이 먼저 실행되느냐와는 아무 관계없습니다.

이를테면 (a-b)-c를 a-(b-c)라고 쓴다고 해서 b, c가 a보다 먼저 계산되는 게 아닙니다. a, b, c가 만약 함수 콜이라면 그중에 어느 함수부터 부를지는 컴파일러 마음대로입니다.

그래서 같은 변수에 ++를 두 번 하는 수식은 컴파일러가 마음대로 아무 값이나 줄 수 있습니다. 그런 수식을 안 쓰는 게 답입니다.

익명 사용자의 이미지

컴파일러마다 어떤 표준을 쓰느냐에 대한 차이점은 있겠지만 gcc나 vc 둘다 저건 연산자 우선 순위에 의해서 결정되는게 아닌가요?

익명 사용자의 이미지

이항 연산자는 기본적으로, 어느 항부터 먼저 평가해야 하는가에 대한 문제가 있습니다.

(A) + (B)

수학에서는 (A)항부터 먼저 계산하던 (B)항부터 먼저 계산하던 결과가 같기 때문에, 상관이 없습니다.
하지만 C에서는, 종종 A나 B항이 객체의 값을 수정한다던가 함수를 호출하는 등의 side effect를 동반하는 경우가 많기 때문에
이 순서가 대단히 중요합니다.

언어에 따라서는 이 순서를 정해 놓은 경우도 있습니다만,
C언어에서는 각각의 상황에 맞는 최적화를 위해서, 이 순서를 자율에 맡기고 있습니다.

그리고 또한, 이러한 상황에서의 처리를 위해 sequence point라는, 그 전까지의 side effect가 모두 처리가 되는게 보장되는 지점을 따로 정의하고 있습니다.

이건 설명이 길어지므로, 그냥 검색해 보시는 편을 권합니다.
고정관념을 버린다면, 이해하기 어려운 개념은 아닙니다.

익명 사용자의 이미지

이해를 위해 예를 하나 들겠습니다.

(A) + (B) * (C) 라는 수식이 있다고 칩시다.
(B)*(C)의 결과값에 (A)를 더하는 것은 연산자 우선순위에 의해 보장이 됩니다.
하지만 (A), (B), (C) 라는 부분 수식 중에 어느게 먼저 평가될지는 unspecified입니다.
즉, (A)를 미리 처리한 후에 그 결과값을 레지스터에 저장해 놓고, 그 다음에 (B)와 (C)를 처리해도 아무 상관 없습니다.

gilgil의 이미지

Visual Studio 2012 Debug 모드에서 컴파일해 봤습니다.
다음과 같은 결과가 나오네요.

; 7    : 
; 8    :   a =
; 9    :     ++a
; 10   :     +
; 11   :     ++a;
 
	mov	eax, DWORD PTR _a$[ebp]   ;
	add	eax, 1                    ;
	mov	DWORD PTR _a$[ebp], eax   ; a에 1을 더해서 a에 넣는다(9라인) : a에 1이 들어 감 --- (가)
	mov	ecx, DWORD PTR _a$[ebp]   ;
	add	ecx, 1                    ;
	mov	DWORD PTR _a$[ebp], ecx   ; a에 1을 더해서 a에 넣는다(11라인) : a에 2가 들어 감 --- (나)
	mov	edx, DWORD PTR _a$[ebp]   ; 
	add	edx, DWORD PTR _a$[ebp]   ;
	mov	DWORD PTR _a$[ebp], edx   ; (가)와 (나)의 값을 더해서 a에 넣는다(8라인) : a에 4가 들어 감 --- (다)

결론 : 애매모호하게 코딩하지 말자. ^^

익명 사용자의 이미지

단항 우선인데 실수했네요.

익명 사용자의 이미지

헷갈리네요 갑자기

익명 사용자의 이미지

제대로 알고 싶으면 sequence point에 대해 검색해보세요.

이상하게 사람들이 이걸 자신의 직관과 다르다는 이유, 혹은 자기 역어셈 결과하고 다르다는 이유만으로 잘 받아들이질 못하더군요.

gilgil의 이미지

결과가 3이 아닌 4가 나오는 이유는
"Prefix increment operator가 수행이 될 때, object가 아닌 reference를 반환하기 때문이다"라고 결론을 내릴 수 있겠네요.

자세한 설명을 해 보았으니 참고하시기 바랍니다.
http://www.gilgil.net/57362

klenui의 이미지

그러고 보니 자연스럽던 ++이 정말 기괴하게 보이네요...

라스코니의 이미지

이 쓰레드를 쭉 읽어보니 side effect 가 생각나네요.

MISRA 던가 거기에서 가급적 side effect가 없도록 코딩하라고 권고하던게 생각나네요.

b = ++a + ++a; 는 정말 side effect 가 많은 코드죠.

뭐 아래 정도가 적당히 나눈 게 될까요?

++a;
b = a;
++a;
b += a;

gilgil의 이미지

으허허... 전 괜히 어렵게 설명을... ㅠㅠ

vani2의 이미지

오오 ..

정말 정성스럽게 설명을 해주셨네요.

덕분에 모든 궁금증이 풀렸습니다. 감사합니다 :)

익명 사용자의 이미지

위 상황에 ambiguity 따윈 없습니다. 다만 사람들이 해당 규칙에 대해 잘 알지 못할 뿐입니다.

C언어에서는 두 sequence point 사이에서 동일 객체가 두번 이상 수정되거나,
한번 수정되고 한번 이상 읽히는 일이 없도록 하라고 분명하게 정해 놨습니다.

C언어는 간결하긴 하나, 직관적이진 않습니다.
직관적으로 생각해서는 안되는 함정들이 곳곳에 숨어 있습니다.

그리고 절대로, 절대로 역어셈 결과에 의존해서 언어 자체의 규칙을 추론하지 마세요.
귀납법의 약점이 그대로 적용됩니다.

cleol의 이미지

틀린 설명입니다.
위에 익명님 말씀이 맞습니다.
예를 들어 intel compiler로 컴파일하면 결과로 3을 줍니다. clang도 3을 주고요. g++은 4를 주지요.
컴파일러마다 다릅니다. 이미 여러 분이 말씀하셨듯이 정의되지 않은 동작이기 때문입니다.
g++에 Wall 옵션을 주면 해당 라인에 대해서 warning: operation on ‘a’ may be undefined 라는 경고를 냅니다.
clang도 Wall 옵션을 주면 warning: multiple unsequenced modifications to 'a' 라는 경고를 냅니다.
(icc와 vc++은 Wall 옵션을 줘도 아무 경고가 없군요.)
다른 분들이 말씀하셨듯이, 또 이 경고들을 보더라도 sequence point에 대해 알아보시는 것이 좋겠습니다.

vani2의 이미지

그렇군요..

조금더 찾아봐야겠습니다.

gilgil의 이미지

음... 그렇군요.
컴파일러에 따라서 결과가 다르게 나올 수도 있네요.
좋은 정보 감사합니다. ^^

익명 사용자의 이미지

a = ++a + ++a;

이 코드는 컴파일러에 따라서 결과가 다르게 나오는 코드가 아니라, 그냥 잘못된 코드입니다.
지금 생각하고 계신 의미는 implementation-defined나 unspecified에 가까운데,
저 코드는 그것들에 해당하지 않습니다.

익명 사용자의 이미지

같은 컴파일러에서도 경우에 따라 다른 결과가 나올 수 있습니다.

익명 사용자의 이미지

불량 식품을 먹으면 어떻게 되느냐는 질문에
배가 아프고, 설사를 유발할수 있다라고 답변하는게 정상잊린데
그냥 "불량 식품을 먹으면 안된다"라고만 하는 답변이 보이ㄴ요.
그걸 누가 모르나

sohn9086의 이미지

세상에는 불량식품을 먹어도 되는지 안 되는지보다 더 심각하게 고민해야 할 문제가 많습니다.

생산적인 댓글을 달자

익명 사용자의 이미지

잘못된 비유는 집어 치웁시다.
자기가 잘못 알고 있다는게 뭔가 감정적으로 불편하신 상황인거 같은데,
이런 상황이면 비유를 통한 설명은 서로 상대의 비유에 대한 말꼬리 잡기로밖에 안끝나겠지요.

그러니 그냥 해당 내용 자체를 기술적으로 이야기하도록 합시다.

a = ++a + ++a;

위에서, 이게 왜 4가 나오는지를 설명한 내용들은 다 틀렸습니다.
예를 들어 gilgil님은 전위 ++ 연산자가 object를 반환하면 3이고 reference를 반환하면 4다 라고 설명했는데,
이건 잘못된 해석입니다.

C++과 달리 C의 전위 ++연산자는 객체 그 자체를 반환하는게 아니고 결과값만을 반환하는데,
gilgil님의 해석대로라면 (C++이 아닌)C 컴파일러는 전위 ++ 연산자가 object(copy)를 반환하므로,
그렇다면 '무조건' 결과값이 3이 되어야 할 것입니다.
하지만 제 gcc에 --std=iso9899:1999 옵션을 주고 결과를 봐도 여전히 결과값은 4가 나옵니다.
이건 gilgil님의 해석으로는 설명할 수 없는 결과지요?

이건, reference냐 object냐의 차이에 의해 결과가 달라지는게 아닙니다.

수식을 처리할 때 어떤 순서로 각각의 피연산자들을 읽고, 또 쓰는지,
그리고 또 그 과정에서 컴파일러가 어디까지 어떤 방식으로 최적화를 하는지 등의
굉장히 다양한 요소들이 그 결과에 관여하게 됩니다.

무엇보다 가장 큰 문제는, 이게 undefined behavior(주의: C표준에서는 특별한 뜻을 가진 용어입니다)라서
컴파일러 제조사가 어떤 동작을 보일지를 책임지지 않는다는 겁니다.
표준에서 undefined behavior에 대해 설명해 놓은 부분을 인용하겠습니다.

NOTE : Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

이런 상황에서 그 결과가 왜 그렇게 나오는지를 따지는 것은 대체로 무의미합니다.
그래서 잘못된 코드라고만 말하는 겁니다.

익명 사용자의 이미지

근데, 그러고보니 sequence point에 대해 찾아보라고 위에서 말씀드린거 같은데...

그거면 충분히 원하시는 내용들에 대한 설명이 될텐데요...

gilgil의 이미지

에구... 제가 괜한 논란을 일으킨 것 같아서 죄송한 마음이 드네요.
본 스레드 덕분에 저도 sequence point에 대해 알게 되어서 개인적으로는 도움이 된 것 같습니다.
그냥 긍정적으로 생각하시면 좋을 것 같은데요. ^^

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.