백준 온라인 저지[BOJ]에서 10942번 문제를 풀던 중, 며칠에 걸쳐 '우연히' 알게 된 사실!
bool dp[2001][2001];
//
// 중략
//
cout << dp[s][e] ? "1\n" : "0\n";
마지막에 답을 출력하는 형태로 위와 같이 작성했었는데, 계속 오답처리 되었다.
알고보니 operator<< 가 삼항연산자보다 우선순위가 높게 "규칙이 정해져 있었기 때문이다."
"규칙이 정해져 있다" 라는 개념이 당연하게 들리겠지만,
규칙이 정해져 있지 않은 영역에 의해 발생하는 문제인 이식성(portability)을 들여다 보고나면,
표준규칙이 안정감이 느껴지는 내 집처럼 느껴지게 된다.
이 글 후반부에서 다루도록 하겠다.
규칙에 의해 위의 코드는 이렇게 인식된다.
(cout << dp[s][e]) ? "1\n" : "0\n";
의도대로 출력하기 위해서는 아래와 같이 코드를 수정해야한다.
cout << (dp[s][e] ? "1\n" : "0\n");
결론은 간단하지만 이 사실을 알기 위해 험난한 길을 돌아왔다.... 부끄럽지만 아직도 기본기가 많이 부족하다.
간혹 BOJ 문제를 풀다보면 VISUAL STUDIO 에서는 잘 되지만, 백준사이트에서는 안되는 경우들이 존재한다.
string 변수가 한 예이다. VS에서는 헤더파일을 include 하지 않아도 string 변수가 잘 선언된다. 하지만 백준사이트에 그대로 제출하게 되면 컴파일 에러를 받는다. string 헤더파일을 include 해야한다. 이런 경험들 때문에, 위의 문제 또한 그런 종류의 문제가 아닐까 하고 생각을 했다.
여기서부터 험난한 모험이 시작된다....
(IDE / 컴파일러) 의 차이에서 발생하는 결과군!
↓
① cout객체 ? 이식성?
↓
해당 IDE에서 제공하는 헤더파일 소스코드를 뜯어봐야겠다!
↓
VS 의 소스코드를 보자!
↓
② 소스코드가 없네?? 어디서 볼 수 있지??
↓
③ 백준 사이트의 컴파일러는 뭐지?? GCC ? GNU ?
↓
④ GCC 에서 다시 실행 후 결과보기!
↓
operator<< 연산자 우선순위... 였구나...! 규칙!
대충봐도.. 난해하다... 맨 땅에 헤딩한다고 하나 허허
그런데 이렇게 고생하면서 단계별로 얻은 정보들이 꽤 많았다.
① iostream 의 멤버함수와 객체에 대해 검색해보자!
- 조건문 안에 표준 입출력 객체가 위치하는 것을 본 적이 있다. if ( scanf("%d",&i) != eof ) 와 같은 경우.
- 위의 경우도 삼항 연산자 안에 위치하여 조건문 안에 위치한다. 표준 객체가 다른 역할을 수행할 수 있겠다는 추측을 해보았다.
- 검색 결과 : operator<< 도 함수다. 연산자 함수는 결과값을 반환한다. 평소에는 cout 객체를, 조건문 안에서는 bool 값을 반환한다. 이는 iostream 내에 conversion operator가 정의되어 있기 때문이라고 한다. 이 conversion operator는 조건문 안에 있거나, 명시적 형변환을 하는 경우에 작동하는 듯하다.
(변환연산자 - conversion operator : 여기와 여기 참고)
- cin / cout 을 다른 형식의 변수에 복사,대입하는 것은 허용되지 않는다. stream 클래스가 복사 기능을 허용하지 않기 때문이다.
cout.operator<<(a); // cout << a; 와 같다. <<도 함수이다!
if(cin >> a) //... 입력에 성공하면 t / 실패하면 f
bool b(cin >> a); // 가능
bool b = cin >> a; // 불가능!
bool b = static_cast<bool>(cin >> a); // 가능
- 검색 도중 이식성에 대한 내용 등장! ( 여기 )
- 아래의 코드를 보자. 출력 결과가 어떻게 될까?? 136이라고 생각한다면 반은 맞고 반은 틀린셈이다. 컴파일러의 종류에 따라 136이 되기도 하고, 666이 출력되기도 한다. VS에서는 한 줄 함수가 연속적으로 호출될 때 ;를 만나기 전까지 모든 값을 뒤에서부터 계산 후 스택에 저장했다가 한번에 호출한다!! 그래서 변수 i는 6이 되어 한번에 출력이 되는 것이다! 반면 GCC에서는 순차적으로 함수가 호출되어 136이 출력된다.
- 하지만 처음 이 글을 접하고는 컴파일러마다 다르구나~ 하고 어렴풋이 이해하고 넘어갔었다. 그래서 컴파일러의 작동 방식을 알아보고 싶었지만,, 어셈블리 코드로 분석하는 법은 몰라서, 일단 소스코드를 열어보기로 했다.
(뒤에서 얘기하겠지만 VS의 소스코드는 확인할 수 없었다. 유료버전이라 감추어 놓은 듯하다.)
#include <iostream>
using namespace std;
int main() {
int i = 0;
cout << (i = i + 1) << (i = i + 2) << (i = i + 3); // 136 또는 666 출력!
}
② 비주얼 스튜디오(Visual Studio)의 소스코드!
- 헤더파일의 위치 : C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\include
- 아무리 찾아봐도 헤더파일 안에 소스코드가 있는걸 찾아볼 수 없었다.
- 소스코드는 안보이고 대부분 메크로로 이루어진 파일이었다.
③ BOJ 사이트에서 이용하는 컴파일러 GCC / 소스코드
- 알아보니 백준온라인저지 사이트에서는 GCC 컴파일러를 사용하고 있었다. ( c/c++ 에 한하여. )
( BOJ에서는 해당 언어의 컴파일러 정보를 공개하고 있으니 확인해보자 - 이곳 )
- 검색 도중 GCC, GNU, LINUX 등의 용어가 이 시점에서 마구 보이기 시작했다. 간단하게, 좋은 철학을 가진 사람들이 모여 열심히 만든 제품을 오픈소스로 제공하는구나! 하고 이해하고 있다. 알아보면 더 많은 것들이 보일법한 주제다. 나중에 시간이 되면 좀 더 파봐야겠다.
- GCC 헤더파일 소스코드 찾았다. 오픈소스 기반이다 보니 공개가 되어있다. (이곳과 이곳)
- GCC 기반 IDE 프로그램 (www.bloodshed.net/)
- GCC 컴파일러를 이용하는 온라인 코딩 사이트를 몇 개 찾았다!
④ ANSI / ISO 표준
- 위 사이트에서 코드를 실행하고 결과를 확인했다. 처음 말했던 대로 연산자 우선순위의 문제였다. 연산자 오버로딩을 통해 구현된 함수라도, 그 우선순위는 유효하다. ( 링크 )
- 개발 환경에 따라 결과 값이 달라지는 것을 최소화하기 위해 만들어진 표준이 존재한다. 국제 표준 기구(International Standards Organization, ISO)와 미국 국가 표준 기관(American National Standards Institute, ANSI) 에서 C++ 프로그래밍에 대한 여러 표준을 만들었다. 이 표준은 어떤 회사가 IDE 툴을 제작할 때 꼭 지켜야만 하는 가이드라고 봐도 된다. 표준화 작업 결과물 중의 하나가 바로 STL 라이브러리이다. 다음 링크를 확인하면 된다.
- http://www.cplusplus.com/
- http://ko.cppreference.com/w/
- http://msdn.microsoft.com/ko-kr/library/52cs05fz%28v=vs.100%29.aspx
- 위에서 보았던 예시는, 위의 ANSI 표준 / ISO 표준에 해당하지 않는 부분이었기 때문이다. 표준이 없으니 컴파일러마다 구동하는 방식이 다를 수 밖에 없다.
고생 후기
- 며칠간 들여다 본 수고가 고작 연산자 우선순위를 잘 몰랐기 때문이라니... 라는 생각에 허탈했지만, 그 과정을 통해 허탈감만 얻었던 것은 아니었다. 여러가지 몰랐던 구석구석 작은 정보들을 많이 알게 되었다. 그냥 흘러가게 두고 싶지 않아서 이 글에 남겨두었다. 이식성이라고 하는 개념이 이런 수고들을 통해 다가와서 그런지 더 피부로 와닿는다. 이식성이 확보되지 않는다면, 한 언어로 만든 프로그램이 다른 사용자들의 데스크탑, 휴대폰 등에서 의도치 않은 오류를 일으킬 수 있겠다. 지금까지는 가독성이 좋고, 시간복잡도와 공간복잡도를 최소화하는 코드가 좋은 코드라고 생각했었다. 그런데 여러 환경에서 나의 코드를 실행시켰을 때에도 동일한 결과를 얻을 수 있는가에 대한 고민도 하게 되었다. 좋은 코드란 무엇인가..에 대한 고민인듯 하다.
- 이식성에 대해 곰곰히 생각하고 고민하고 검색도 해보면서, 말로는 설명할 수 없는 어떤 느낌을 받았다. 컴퓨터 언어마다 느껴지는 질감이랄까.. C언어는 하드웨어적인 부분까지 직접 코딩할 수 있어서 그런지 거칠거칠한 느낌이 많이 드는 반면, JAVA는 더 부드러운 느낌이 많이 든다. OS 개발이나 시스템 프로그래밍에 C언어를 많이 사용한다고 하는데, 왜 그런지도 알 것 같다. 또 JAVA는 이식성이 좋은 언어라는 것도 발견했다. 가상머신 때문인가..? 아직 운영체계에 대해서 잘 알지 못해서 그 이유에 대한 구체적 근거는 알지 못하지만, 언어별 시스템별 느낌이 느껴지기 시작했다! 뿌듯하다!
'이러쿵저러쿵' 카테고리의 다른 글
정보보안 랩실 학부연구생 기록 (6개월) (0) | 2023.01.29 |
---|---|
[대학원] 포스텍 컴퓨터공학과 합격 후기 (자소서 첨부) (6) | 2023.01.29 |
무엇을 위한 개발인가.. (0) | 2022.05.09 |
군생활 중 공정근무프로그램 만들었던 이야기.. (5) | 2022.04.30 |
[삼성전자 X 관악구] 멘토링 후기 (0) | 2021.04.04 |