[c++]cout의 객체반환에 대해서
글쓴이: again4you / 작성시간: 일, 2004/02/29 - 8:50오후
물어봐도 속 시원하게 답변해주는 사람이 없어서 염치 불구하고 여기에 올리게 되었습니다.
다음과 같은 간단한 코드에서 이상한 결과가 나오네요
#include <iostream.h> int main() { int update=6; int *p_update; p_update=&update; cout << "update is " << update << " and address update is " << &update << endl << "*p_update is " << *p_update << " and address *p_update is " << p_update << endl << "++p_update is " << ++*p_update << endl; return 0; } 결과 update is 7 and address update is 0x0012ff7c *p_update is 7 and address *p_update is 0x0012ff7c ++*p_update is 7
여기서 제가 원하는 결과는 update = 6, *p_update = 6, ++*p_update = 7을 생각하고 있었는데 전혀 엉뚱한 결과가 나오네요
여기서
cout << "update is " << update << " and address update is " << &update << endl << "*p_update is " << *p_update << " and address *p_update is " << p_update << endl; cout << "++p_update is " << ++*p_update << endl;
위와같이 cout을 독립적으로 적으면 원하는 결과가 나오는 것를 확인했습니다.
그럼 cout 에서 반환되는 객체에 제가 모르는 부분이 있어서 어런 결과가 나오는거 같은데 찾아보고 물어보아도 다들 모른다고 하네요
설명을 부탁드립니다.
아니면 제가 공부해야 할 부분에 대해서 알려주시면 찾아 보도록 하겠습니다.
그럼 좋은 하루 보내세요
===============================================
독도는 우리땅!!
Forums:
뒤에서 부터 계산이 되는 겁니다. (위에것)++*p_update가 가
뒤에서 부터 계산이 되는 겁니다. (위에것)
++*p_update가 가장 먼저입니다.
함수의 인수 전달 방식과 스택이 LIFO의 특성을 갖는 것 때문에 그렇습니다.
즉, 예를들어서
func(a,b,c);
이런게 있다면, push c, push b, push a 후에 func을 call합니다.
[code:1]func(a,b,c);[/code:1]
func(a,b,c);
push c
push b
push a
call func
* 오른쪽에서 왼쪽으로 진행.
염두에 두소서..!^^
아는 부분이 나와서..위에서 언급한 부분...
등급좀 올리려고 몇자 적어봅니다^^
-------------------
나는 Copy&Paster 이다. 나의 화려한 기술 조합에 모두들 나를 두려워 한다. 나도 코드 Maker 이고 싶다.
함수에 인자를 전달하는 방식과는 상관이 없는 것 같습니다.위의 설명대
함수에 인자를 전달하는 방식과는 상관이 없는 것 같습니다.
위의 설명대로라면...
결과가 위처럼 되야할 것입니다.
질문된 코드를 다음과 같이 바꿀 수 있습니다.
즉 cout 객체의 함수 operator<<() 에 인자 하나씩만 전달해서 호출합니다.
따라서 실행 순서는
입니다. 그런데 출력이 왜 그렇게 나올까요... cout 은 바로 출력하지 않고 출력 버퍼에 값을 보냅니다.. 그래서 그런 것이 아닌가요? 출력 버퍼에서 변수는 변수의 주소를 가지고 있으므로 마지막에 (12) 번 호출에서 값이 바뀌면 그 값이 그대로 찍히는 것 같습니다.
인생, 쉬운 것만은 아니네..
cout << 'a' << 'b' <<
cout << 'a' << 'b' << 'c'; 는 이렇습니다.
먼저 c가 저장되고 b가 저장되고 a가 저장됩니다. 그리고 cout은 a를 먼저 꺼내고 b를 꺼내고 c를 꺼냅니다. 그래서 출력은 abc가 되죠.
cout<<a<<++a; 식이라면 이렇습니다. ++a가 저장되고 a가 저장되고 cout이 호출됩니다. a를 꺼내고(이미 증가되었죠) ++a를 꺼냅니다.
그래서 LIFO(FILO)인 것입니다.
[quote]cout << 'a' << 'b' &l
(먼저 c가 저장된다는 의미를 << 'c' 멤버 함수가 먼저 호출되는 것으로 이해했습니다)
pyj200 님의 말을 따르면 C++ 언어의 멤버 함수 호출 순서가 뒤에서부터 왼쪽으로란 뜻이 됩니다. c 가 먼저 저장될려면 << 'c' 멤버 함수가 먼저 호출 되어 호출 스택에 먼저 Push 되어야 합니다.
위의 클래스를 예로 들면...
제가 이해한 것을 가정하에 pyj200 님의 말을 따르면 코드의 출력 결과는 122 이어야 합니다. 하지만 출력 결과는 012 입니다.
제가 잘 모르는 것일 수도 있으니 c 가 어떻게 먼저 저장이 되는지 자세히 알려주세요^^
인생, 쉬운 것만은 아니네..
저는 문자 출력을 예로 들려고 했던 것이 아니라 처음 제기됐던 내용을 타
저는 문자 출력을 예로 들려고 했던 것이 아니라 처음 제기됐던 내용을 타이핑 하기 귀찮아서 약식으로 표기한겁니다. cout << 'a' << 'b' << 'c';를 컴파일 한다면 하나로 뭉쳐서 컴파일 되겠죠. 순서를 따질 여지도 없을 겁니다.
스택에 담기고 나오는 순서가 중요합니다.
c++에 대해서는 잘 모르지만 일단 문제제기하신 코드는
프린트1이 호출되고, 2가 호출되고, 3이 호출되지 않습니까? 그럼 1이 호출됐을때 0, 2가 호출되면 1, 3이 호출되면 2가 출력됩니다. 그러고 보니 이건 종류가 다른 예인데요... :/
...같은 내용의 글타래가 이전에 있었던것 같아서 검색해봤는데 못찾았습니다.
http://khdp.org/docs/common_doc/shellcod
http://khdp.org/docs/common_doc/shellcode만들기_0912.txt
좀 다른 분야이기는 하지만 이 문서를 한번 보시면 도움이 될것 같습니다.......
전 곡쓰러 가야 합니다. =3=3=3=3 내일부터 다작모드로 들어가기로 했거든요. 게임음악을 위해 =3=3
하루에 두곡씩 쓸 계획입니다.
아~ 이런이런... pyj200 님하고 하지 않아도 될 논쟁을 벌였습니다
아~ 이런이런... pyj200 님하고 하지 않아도 될 논쟁을 벌였습니다.
디버깅을 돌려보다가 정말 아차하는 순간에 이유를 알았습니다 ^^;;
이유는 전위++ 이냐 후위++ 이냐에 따라 그런 것이었습니다.
즉 그래서
은 함수 << 를 호출하기 전에 ++*p_update 문에 의해서 미리 7 로 증가되었던 것입니다..
VC++ 로 디버깅을 해보면서 처음에 전달되는 값은 "update is" 인데 그 다음 update 변수가 별안간 7 로 되어 있는 것이 아니겠습니까~ .. 왜 그럴까 하다가 저것이 바로 생각나더군요..^^;;
인생, 쉬운 것만은 아니네..
바로 글을 다셨네요..^^;전 게임 프로그래밍에 관심만 많은 넘인데.
바로 글을 다셨네요..^^;
전 게임 프로그래밍에 관심만 많은 넘인데...-_-;;;
멋진 게임음악 만드시길 바랍니다.^^
인생, 쉬운 것만은 아니네..
[quote="pyj200"]스택에 담기고 나오는 순서가 중요합니다.
스택에 담기고 나오는 순서가 중요하지만
C++ 사양은 인수의 평가순서과 마찬가지로
멤버 연산자를 중첩하는 경우
스택에 담기고 나오는 순서를 정의하지 않았습니다.
따라서
int p2=0;
cout<<(++p2)<<endl<<p2<<endl<<(++p2)<<endl<<(++p2)<endl;
라는 코드의 경우 출력값은
컴파일러에 따라서
3
3
3
3
이 될 수도 있고
1
1
2
3
이 될 수도 있습니다.
그러니
문의하신 경우처럼 코딩하는 것은 이식성이 없는 위헙한 코딩입니다.
김형님, pyj200님, mangg님께 진심으로 감사드립니다. 특
김형님, pyj200님, mangg님께 진심으로 감사드립니다.
특히 알려주신 내용 및 문서에 대해서는 더욱 공부해야 할 것 같습니다.
날씨 쌀쌀하신데 건강하시고, 좋은 하루 보내시기 바랍니다.
=============================================
독도는 우리땅!!
굳이 정의하지 않은 이유는 뭔지...
상당히 흥미로운 이야기여서 Assembly code 를 생성해봤습니다.
(언젠가는 저도 같은 실수를 했을지도 모르죠.)
모든 매개변수를 거꾸로 stack 에 쌓으면서 평가한 후 함수를 순서대로 호출하는군요.
C 에서 함수호출은 side effect 가 발생하지 않는 sequence point 에 해당합니다.
그냥 순서대로 호출하면 될 것을 왜 정의하지 않았는지...
또 GCC 는 어떤 연유로 매개변수를 몽땅 stack 에 쌓아 놓는지... -_-
혹시 어떤 이유가 있나요?
성능이 좋아질 것 같지도 않고...
stack 은 사용할대로 사용하고...
[quote]또 GCC 는 어떤 연유로 매개변수를 몽땅 stack 에
GCC 만 특별히 그러는것은 아니고요
C Calling Convention 이라고 하는 C에서 함수 호출시의 특성때문에
그렇습니다.
- advanced -
약간 질문이 다른 것이
C++ 에서 member 연산자 함수를 중첩하는 경우를 질문하는 것입니다.
각 member 연산자 함수는 독립적인 함수호출이잖아요.
중첩된 연산자 함수 호출이 마치 하나의 함수호출처럼 모든 매개변수를 쌓기에 이상하다는 것입니다.
또 한가지 질문이 전연연산자함수는 상관없나요?
Re: 굳이 정의하지 않은 이유는 뭔지...
인수가 필요한 멤버함수를 중첩해서 쓰는 경우
GCC 이건 어떤 컴파일러건 매개변수를 몽땅 stack 에 쌓아 놓습니다.
int i=10;
int j=20;
cout<<(5+(i*j+(30-j)*2)/3)<<endl;
위 코드의 경우 컴파일러가
(5+(i*j+(30-j)*2)/3) 을 계산하기 위해
각 수를 모두 스택에 넣어두고 계산합니다.(5,i,j,30,j,2,3)
같은 원리로 인수가 필요한 멤버함수를 중첩해서 쓰는 경우도
마찬가지이죠
처음 생각으로는 인수가 필요한 멤버함수를 중첩해서 쓸 때
그냥 순서대로 호출하면 될 것이 아니냐고 할 수도 있는데
위 코드 "cout<<(5+(i*j+(30-j)*2)/3)<<endl;" 과 같이
멤버함수를 중첩해서 썼더라도 C++ 에서 한 개의 명령을 구분하는
문자 ";" 을 만나기 전까지 나타나는 모든 인수를 스택에 저장해 놓고
한 번에 계산한다고 생각하시기 바랍니다.
그 이유는 "cout<<(5+(i*j+(30-j)*2)/3)<<endl;" 에서처럼
왜 C++ 은 각 수(앞에서 부터 5,i,j,30 등등) 를 스택에 먼저 저장하고
한 번에 계산하는지에 대한 답과 동일합니다.
주의하실 점은
int i=0;
cout<<(--i)<<i<<(++i)<<(++i)<<(i+100)<<endl;
의 경우
차례대로 (--i,i,++i,++i,i+100) 에 해당하는 수를 스택에 저장할 거라고 생각하지만
꼭 그렇지가 않은 것이
VC++ 의 경우 인수의 메모리 주소 공간이 같을 경우
두 번 저장하지 않습니다.
따라서 VC++ 에서는
(--i,i,++i,++i,i+100) 이 아니고
순서대로 --i, i, ++i, ++i 을 계산한 그 수 i 와 i+100 만 스택에 저장합니다.
즉 (최종 변수 i , i+100) 이렇게 2 개의 값만 스택에 저장하는 것입니다.
이전 글에서도 얘기했지만 이런 것은 어느 쪽이든
C++ 사양에서 정해지지 않은 것이므로
이식성이 없다는 점 주의하세요
전역연산자함수라고 해서 예외가 되지는 않습니다.
읍.. 궁굼한게 하나 생겼는데요...endl 이 flush 도 하지
읍.. 궁굼한게 하나 생겼는데요...
endl 이 flush 도 하지 않나요?
음.. flush 하는것과 스택에 들어가있는 함수를 처리하는 것과는 상관 없는가보군요...
갑자기 컴파일러에대해서 더 알아야겠다는 생각이 막....
하나 배우고 갑니다. 고맙습니다.
답변 감사드리며 마지막으로 한가지 더 묻습니다.
akbar 씨의 답변에 멤버함수라고 이야기하시는데 그렇다면 연산자함수만이 아니라 일반적인 멤버함수도 마찬가지라는 것인가요?
왜인지 일반 전역함수도 마찬가지라는 답변이 나올 것 같기에 더욱 의문입니다.
음... 제가 sequence point 에 대해 잘못 알고 있는 것 같네요.
제대로 공부안한 티가... (T.T)
akbar 씨의 글을 보면 언제나 C++ 의 새로운 가능성을 보는 것 같습니다.
솔직히 이해를 못하는 경우가 허다합니다만... -_-
맞습니다. 합니다. 그냥 '\n' 과는 다릅니다.
Re: 답변 감사드리며 마지막으로 한가지 더 묻습니다.
일반 전역함수도 마찬가지로 작동하며 같은 이식성 문제가 있습니다.
단 일반 전역함수를 멤버함수 중첩하듯이 쓸 수 있을 까요
물론 있죠
C++ 문자열 클래스 string 의 + 연산자가
대개는 전역함수로 선언되어 있습니다.
그래서 아래 코드가 가능합니다.
string cs("Love");
cout<<cs<<(cs="I " + cs + " C++" + " World")<<endl;
cout 는 cs 를 출력하고
다음에 (cs="I " + cs + " C++" + " World") 의 연산결과를
출력하고 있는 듯이 보입니다.
사용자는
처음 cout 이 cs 를 출력할 때 "Love" 를 출력하고
다음에 (cs="I " + cs + " C++" + " World") 의 연산결과를 출력할 때
"I Love C++ World" 가 출력되기를 바라겠지만
꼭 그렇지는 않고
"I Love C++ World" 이 두 번 출력될 수도 있습니다.
왜? 컴파일러에 따라서
최종적으로 인수 cs 가 가리키는 메모리 공간에서 값을 가져오기 때문에
"I Love C++ World" 을 두 번 출력하는 경우가 있게 되는 것이죠.
결국 이식성이 없어요 ...
와.. 정말 많은 것을 배우고 갑니다.전 기본을 다시 딱아야 되겠네요
와.. 정말 많은 것을 배우고 갑니다.
전 기본을 다시 딱아야 되겠네요^^
인생, 쉬운 것만은 아니네..
궁금해서 각 컴파일러 별로 테스트를 해봤습니다.Visual C++
궁금해서 각 컴파일러 별로 테스트를 해봤습니다.
Visual C++ 6.0 SP5
Visual C++ 7.1
Borland C++ 5.6.4
Borland C++ 6.0 Preview
GCC 3.2
재밌는 건 Borland C++ 6.0 Privew만 결과가 다르게 나왔다는 사실입니다.
Borland측 말에 따르면 6.0은 표준 100%를 지킨다고 하더군요.
믿을 순 없겠지만...
[quote]스택에 담기고 나오는 순서가 중요하지만 C++ 사양은
요게 무슨 뜻인지 몰라서 vc++ 7.1.3091 과 gcc 3.3.2 에서
테스트 해 봤는데요..
테스트 코드는 이렇구요..
결과 값이...
으로 나옵니다.
디스 어셈블 해보니...
vc++은 계산을 다 해서 i 값을 만들어 놓고...
(그 과정에서 i=6이 됩니다.)
<< operator를 세번 호출합니다.
gcc 3.3.2에서는 왼쪽부터 순서대로 << operator를 실행하네요...
이걸 보고 생각한건데...
멤버연산자가 중첩된경우 호출 순서를 명시하지 않았다는 말이 이것처럼...
호출 순서가 순차적일수도 있고, 한번에 몰아서(-_-)할 수도 있다는 뜻인것 같습니다.
함수 argument를 항상 stack에만 쌓는 것은 아닙니다.참
함수 argument를 항상 stack에만 쌓는 것은 아닙니다.
참고로 Sparc 같은 구조에서는 argument 몇개 까지는 register로 넘깁니다.
intel 계열에서도 fastcall 방식으로는 argument 두개까지인가는 register로 넘기도록 되어 있습니다.
---
http://coolengineer.com
댓글 달기