c++ 간단한 질문입니다.
글쓴이: hwanikani / 작성시간: 금, 2010/11/26 - 12:51오후
#include
using namespace std;
int getNum()
{
cout << "Getting Number" << endl;
return 77;
}
int main()
{
cout << "1 " << getNum() << " 2" << endl;
}
위 코드 실행시
Getting Number
1 77 2
가 나오는데 Getting Number가 먼저 나오는 이유가 뭐죠?
제 예상으로는
1 Getting Number
77 2
이게 나올줄 알았는데 ㅠ
왼쪽에서 오른쪽으로 실행이 되기 전에 getNum()이 먼저 한번 훑어지더라구요.
요약: cout << "1 " << getNum() << " 2" << endl;) 에서 getNum()이 먼저 한번 훑어(?)지는 것 같은 현상이 일어나는 이유가 뭔가요?
Forums:
getNum() 가 실행되면서 side effect
getNum() 가 먼저 evaluate되면서 side effect 가 발생하기 때문이죠.
왜 getNum()이 먼저 evaluate되는지 아시나요?
cout << "1 " << getNum() 만 function식으로 바꿔보면
operator<<(cout, "1 ").operator<<(getNum())인데
여기서 getNum()이 먼저 evaluate 되는 이유가 궁금해서 그러네요.
정상적이라면 왼쪽에서 오른쪽으로 evaluate 한다고 생각했는데
여기선 getNum()을 먼저 evaluate 한다음에
전체 식을 왼쪽에서 오른쪽으로 다시 진행하더라구요.
뭔가 그 룰 같은게 있나요?
함수 인자가 평가되는 순서는 알 수 없기 때문입니다.
함수 인자가 평가되는 순서는 알 수 없기 때문입니다. 컴파일러 맘입니다.
물론, 그 함수가 시작되기 전에 모든 인자가 평가되어야 하지만, 단지 그것 뿐입니다. 언제, 어떤 순서로 평가될지 전혀 알 수 없습니다.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
cout << "1 " << getNum()
cout << "1 " << getNum() 를
<<( <<( cout, "1 " ), getNum() ) 로 생각할 수 있고,
오퍼레이터 << 의 리턴값은 cout 입니다.
두번째 인수인 getNum() 는 evaluate 되면서 side effect 로 문자열이 출력되고.
첫번째 인수인 <<( cout, "1 " ) 는 evaluate 되면서 cout 가 리턴되고 문자열을 버퍼에 저장하지만 바로 출력되지 않고 statement 가 끝날 때 문자열을 출력하고 버퍼를 flush 하게 됩니다.
제가 테스트 해본 결과
식을 처리할때 함수 안에 argument들을 evaluate할때 오른쪽에서 왼쪽으로 처리해서 그런 결과가 나오는것 같네요.
(스택에 argument들이 오른쪽에서 왼쪽순으로 올라가서 그런가 봅니다).
따라서 <<( <<( cout, "1 " ), getNum() ) 식에서도 getNum()이 먼저 evaluate된다음에
<<(cout, "1 ")이 evaluate 되는거 같구요 (디버그 모드로 보니깐 getNum()을 무조건 먼저 evaluate 하더군요).
아래는 제가 테스트할때 쓴 코드입니다.
#include
using namespace std;
int number()
{
cout << "number() called" << endl;
return 44;
}
int numA(int a)
{
cout << "numA() called" << endl;
return 77;
}
int numB(int b)
{
cout << "numB() called" << endl;
return 77;
}
int numC(int c)
{
cout << "numC() called" << endl;
return 77;
}
void testFunc(int a, int b, int c)
{
cout << "testFunc() called" << endl;
}
int main()
{
testFunc(numA(number()),numB(3),numC(3));
}
실행시 numC(3)이 먼저 evaluate되고 그다음 numB(3), 그리고 numA(number())가 evaluate되더군요.
(결과 출력시:
numC() called
numB() called
number() called
numA() called
testFunc() called)
Stack에 인자가 들어가는 순서(흔히 calling
Stack에 인자가 들어가는 순서(흔히 calling convention이라고 하는 것들)와, 인자가 평가되는 순서는 별개입니다.
Calling convention은 대개, 컴파일러 및 CPU에 따라 정해져 있으며, 보통 해당 architecture ABI 문서에 나와 있습니다.
이에 비해 인자가 평가되는 순서는 알 수 없습니다. (unspecified).
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
이런 코드는 피하는게 좋다고 들었습니다. 이와 비슷한
이런 코드는 피하는게 좋다고 들었습니다.
이와 비슷한 예로,
func_a(func_b(), func_c());
와 같은 것이 있습니다.
10년 전 책에서, func_b, func_c 중에 어떤 것이 먼저 호출이 될지 구현마다 다를 수 있다고 하더라구요.
이식성을 제외하고서라도, 코드를 읽을 때 명확하게 순서가 들어오지 않으면 좋은 코드가 아니잖아요.
내 블로그: http://unipro.tistory.com
제가 이 궁금증이 생긴 이유가...
제가 이 궁금증이 생긴 이유가 책에서 Exception에 대해서 읽고 있는데 아래와 같은 코드가 나오더군요 (간략하게 요약).
#include
using namespace std;
double divide(double x, double y) throw (const char*)
{
if (y == 0)
throw "Can't divide by 0";
return x / y;
}
int main()
{
double x, y;
cin >> x >> y;
try
{
cout << x << " divided by " << y << " is " << divide(x,y) << endl;
}
catch (const char*)
{
cout << "Can't divide by 0" << endl;
}
}
근데 여기서 y값으로 0을 입력하면
cout << x << " divided by " << y << " is " << divide(x,y) << endl; 에서
"x divided by y is "는 출력을 안하고 바로 divide(x,y)에서 exception을 잡아 내더라구요.
(divide(x,y)를 먼저 evaluate한다는 뜻)
그것때문에 궁금증이 생기더라구요.
인수의 evaluation 순서와 무관하게, cout
인수의 evaluation 순서와 무관하게,
cout << ... << ...; 에서
문자열이 출력되는 시점의 문제입니다.
계속 질문해서 죄송합니다 ^^;;
아까
cout << "1 " << getNum() 를
<<( <<( cout, "1 " ), getNum() ) 로 생각할 수 있고,
오퍼레이터 << 의 리턴값은 cout 입니다.
두번째 인수인 getNum() 는 evaluate 되면서 side effect 로 문자열이 출력되고.
첫번째 인수인 <<( cout, "1 " ) 는 evaluate 되면서 cout 가 리턴되고 문자열을 버퍼에 저장하지만 바로 출력되지 않고 statement 가 끝날 때 문자열을 출력하고 버퍼를 flush 하게 됩니다.
라고 적어주셨는데
첫번째 인수인 <<(cout, "1 ")이 evaluate된다면 거기서 문자열이 출력 되야 하지 않나요?
cout << "a1" << "a2" << "a3"
cout << "a1" << "a2" << "a3" ; 에서
a1, a2, a3 가 차례대로 출력되는 것이 아니고, 각각이 버퍼에 저장된 후 마지막에 일괄적으로 출력되기 때문입니다.
다음 코드를 실행해 보면 알 수 있을 듯.
버퍼 문제는 아닌것 같네요...
버퍼 문제는 아닌것 같네요...
string F()
{
string temp;
cin >> temp;
cout << "your input is " + temp << endl;
return temp;
}
int main()
{
cout << "You have input " << flush << F() + ", " << F() + ", " << F() << endl;
return 0;
}
flush를 시켜봐도 똑같은것 같네요.
.
flush 는 output 을 하고 버퍼를 비웁니다.
따라서 위 경우는 flush 가 있으나 없으나 차이가 없습니다.
문자열이 화면에 출력되는 시점은 cout 를 포함하는 statement 가 끝나는 순간인데,
개행 문자나 flush 를 이용해 미리 출력할 수는 있습니다.
계속 질문해서 죄송합니다 ㅠㅠ
죄송하지만 문자열이 화면에 출력되는 시점이(버퍼가 되는 시점) cout을 포함하는 statement가 끝나는 순간이라고 어디 나와 있나요?
(싸우려는 말투가 아니라 정말 궁금해서요 ㅠㅠ)
slee님 답글 보다가 갑자기 buffer관련 궁금증이 생겨서 열심히 뒤져 보고 있는데 (관련 질문도 올림;; http://kldp.org/node/119536)
책이든 msdn이든 열심히 뒤져봐도
1. endl
2. flush
3. cin 입력 대기중
4. 버퍼 사이즈 꽉 참.
이 네가지 컨디션이 아니면 flush가 자동으로 된다는 법이 없더라구요.
혹시나 cerr처럼 unitbuf 플래그가 set되어 있는줄 알았는데 (http://www.cplusplus.com/reference/iostream/manipulators/unitbuf/)
cin하고 cout은 unitbuf 플래그가 set되어 있는것도 아니더라구요...
이상하게 자동으로 flush가 되는 이유가 뭐죠?
아 일단, 저 위에 개행문자가 아니고,
아 일단, 저 위에 개행문자가 아니고, endl.
버퍼를 출력하는 시점에 대한 자료를 찾을 수가 없네요.
appropriate time 에 버퍼를 출력한다고 하는데 좀 더 검색해 봐야 할 듯.
...
위에서 다들 잘 써주셨지만...
cout << "1 " << getNum() << " 2" << endl;
이렇게 쓰면 헷갈리니까 <<를 왼쪽부터 함수 f1, f2, f3, f4로 바꾸면,
f4(f3(f2(f1(cout, 1), getNum()), 2), endl)
이렇게 되지 않습니까?
여기서 확실히 보장이 되는 선후관계는 다음뿐입니다. (일단 인자를 다 계산해야 그 인자를 쓰는 함수를 부르겠죠.)
f1->f2->f3->f4
getNum->f2->f3->f4
보시다시피 getNum은 f2를 부르기 전에만 부르면 될 뿐 f1을 실행하기 전에 먼저 불러도 아무런 이상이 없습니다.
.
...
댓글 달기