전체 페이지뷰

2014년 10월 14일 화요일

4. C에서 C++로.....(3)

< C에서 C++로 >

  • 자료형 bool
  • 참조자

안녕하세요. 벌써 네번째 강의의 시작입니다.
오늘은 C++에 새롭게 추가된 자료형 bool 그리고 참조자(reference)에 대해서 알아보도록 하겠습니다. 조금 이해하기 어려울수 있겠지만 노력하면 안될 것 은 없다고 봅니다. 힘냅시다!

(출처 : C++ for everyone 2nd edition)

bool을 알아보기에앞서 우리는 먼저 true(참)과 false(거짓)에 대해서 확실하게 알고 넘어가야합니다. C에서는 우리는 보통 매크로상수를 이용하여 참을의미하는 숫자는 1거짓을 의미한는 숫자는 0 로 사용을 하였었죠.

#define TRUE  1
#define FALSE   0
이렇게 계속사용하다보니 무의식적으로 우리는 TRUE =1, FALSE=0 으로 생각하게 되는데 TRUE는 반드시 1이어야하는것은 아니고 FALSE 또한 반드시 0이어야하는것도 아니랍니다.
단순히 참과 거짓을 구별하려고 사용한것이기 때문이죠. 앞으로 는 참과 거짓을 숫자의 의미로서 생각하지마시고 말의 의미 그대로 참, 거짓으로 생각하길 바라겠습니다.

bool이란 무엇일까?
C++에서는 C에서와는 달리 bool이라는 자료형을 새롭게 추가하였습니다. 이 자료형은 단 두가지의 값만을 가지게됩니다. 바로 "True" 와 "False" 입니다. 말그대로 참과 거짓을 의미합니다. 
bool은 int, double과 마찬가지로 기본자료형과 같기 때문에 보통 변수를 선언할때 처럼
bool Love = true;
bool Love = false; 
이렇게 선언과 초기화가 가능하답니다.

위에 그림에 보시면 boolean Truth table(부울 진리표)가 있습니다. bool형에대한 AND, OR 연산에 대한 결과표 인데요. (bool도 자료형이기에 연산이 가능합니다.)
AND 연산일때는 거짓이 하나라도 있으면 거짓, 모두 참일때만 참.
OR 연산일때는 참이 하나라도 있으면 참, 모두 거짓일때만 거짓.
이렇게 생각하시면 됩니다. 

가장 아래에 표를 보시면 연산자들과 우선순위에 대한 설명이 나옵니다. 아까 말했듯이 bool도 자료형이기에 기본자료형과 동일한 방식으로 사용을 할 수 있습니다.


자그럼 바로 사용해봅시다.



CheckNum 함수를 보도록 하겠습니다. 반환형은 bool , 매개변수를 int num으로 받습니다. 그리고 받아온 num이 0보다 작으면 false를 크면 true 를 출력하는군요.
이전에 함수를 정의해오던 방식과 같습니다. 중요한점은 반환형이 bool 이기때문에 true 
또는 false가 return 되야 한다는점입니다. 아시겠나요?

메인함수에서 키보드로부터 입력받은 숫자를(isnum) CheckNum 함수를 이용해서 0보다 큰지 작은지 판단후에 true 와 false 를 저장해줍니다.(bool Number에)
그리고 if-else 조건문을 이용해서 양수인지 음수인지 화면에 출력을 해주게 됩니다.
아주 쉬운 부분이라 문제없이 이해되실겁니다~
bool에 대해서는 이정도로 알고 넘어가도록 할게요.


다음은 참조자(Reference)에 대한 부분을 다루어 보겠습니다.
참조자도 C++에서 처음등장하는 개념으로 다소 생소한 부분이니 정확하게 알고 넘어가셔야 합니다.

'참조자' 는 말 그대로 참조를 하는 녀석입니다. 어떤 방식으로 참조를 하는 것일까요?
간단하게 예를 들어 보겠습니다.

제 이름은 김철수 입니다. 친구들은 저에게 "직소" 라는 별명을 지어줬습니다. 그래서 친구들은 저를 부를때 철수야 또는 직소 라고 부른답니다.
이 문장을 보면 김철수 = 직소  이렇게 볼 수 있습니다. 그러므로 철수를 부를 때 "철수야!" 라고 해도되고 "직소!" 이렇게 불러도 철수는 고개를 돌리며 "응?" 이라고 대답하는 것이겠죠. 참조자도 바로 이런 역할을 한답니다.

컴퓨터가 알아들을수 있게끔 이야기를 바꾸자면
변수 A에 100이라는 숫자가 저장되어있다. 참조자를 사용해서 A의 별명을 B로 지어주었다. 그랬더니 B를 호출해도 100이라는 숫자를 얻어올 수 있었다. 정도가 됩니다.
참조자의 개념에 대해서 어느정도 이해가 되시나요?

이제그럼 참조자를 사용하는법을 알아보겠습니다.




int A = 100; (변수 A에 100이라는 숫자가 저장되어있다.)
int &B = A;  (참조자를 사용해서 A의 별명을 B로 지어주었다.)
cout << B << endl; (그랬더니 B를 호출해도 100이라는 숫자를 얻을수 있었다.)

이렇게 사용하는게 참조자입니다. 해당 대상에 별명을 지어 주는것이죠.
위 코드를 보면서 머리를 갸우뚱하게 만들만한 부분이 있습니다. 바로 "&B" 이 부분인데요. 우리는 이전에 & 연산자를 변수의 주소값을 반환하는 연산자로 사용해왔습니다. 하지만 여기서 & 연산자는 전혀 다른 의미로 사용이 되어지는 것을 알 수가 있습니다. 이 둘을 어떻게 구분 하는지는 다음을 따릅니다.

* 이미 선언된 변수의 앞에 &연산자를 사용하면 주소 값의 반환
* 새로 선언되는 변수의 이름 앞에 &연산자를 사용하면 참조자의 선언

int *ptr = &A; (A의 주소값을 반환해서 포인터 ptr에 저장)
int &B = A;  (변수 A에 대한 참조자 B를 선언)
그러므로 B는 A의 참조자가 되는 것입니다. 

(*포인터의 개념과 교차시켜 스스로 골머리를 앓게 하시면 안됩니다. 포인터에 개념은 잠시 넣어두세요)

참조자는 변수로 보아야하는가? 참조자가 하는 기능, 연산, 결과 등은 변수와 동일합니다. 하지만 C++에서는 참조자와 변수를 구분해서 이야기 합니다.

참조자의 주소값에 대한 예제를 하나 보도록 하겠습니다.




위 예제를 보면 참조자에도 역시 &연산자를 사용하면 참조자의 주소값을 가져올 수 있다는걸 알수가 있습니다. 그리고 변수 a의 참조자 ref1, ref2, ref3 들의 주소값 역시 변수 a의 주소값과 동일 하다는 것을 알 수 있죠. 왜냐면, ref1, ref2, ref3은 변수 a의 별명이니까요. 

주소값이 모두같다는건 변수와 참조자들이 같은 공간을 가지고 있다는 소리가 됩니다. 그저 a를 부르게되는 별명만 늘어나는 것 입니다. 또 참조자는 선언할 수 있는 갯수에 제한이 없습니다.(별명이 여러개라고 문제가 안되듯이..) 위와 같이 여러개의 참조자를 선언 해도 문제가 없습니다. 하지만 엄청많이 참조자를 선언하면 본인도 사용하는데 까먹어서 정신이 없을거에요ㅋㅋ

참조자를 선언할때는 중요한 점이 있습니다. 참조자는 앞서 누누히 말했듯이 '변수에 별명을 지어주는것이다' 라고 했습니다. 만약 변수가 아니면? 참조자는 선언이 불가능 하겠지요.

int &ref = 100;
int &ref = NULL;
이 참조자의 선언은 유효할까요? 유효하지 않습니다. 왜냐면 변수를 참조하는게 아니라 상수(number), NULL을 참조했기때문입니다.

int &ref;
여기서 &ref의 선언은 유효할까요? 이것도 역시 유효하지 않습니다. 왜냐면 참조자는 반드시 참조할 대상이 함께 존재해야만 의미가 있습니다.

int &ref = *ptr;
int &ref = arr[0];
이렇게 선언된 참조자는 유효할까요? 이 둘은 유효하답니다. 포인터도 역시 변수며 배열요소도 변수로 간주되어 선언이 가능하답니다. 이점 꼭 알아두시기 바래요.

참조자를 이용한 Call by Reference
C언어에서 우리는 두 수를입력받아 그값을 서로 바꾸어 주는 Swap() 함수를 만들면서 포인터의 활용에대해 이해를 하였습니다. 기억나시죠?
그때의 기억을 잠시 되짚어보면, Call by ValueCall by Reference 이 두가지가 떠오를겁니다. C++ 에서는 참조자를 이용한 Call by reference의 함수호출을 지원합니다. 포인터를 이용해서 만들었던 swap 함수를 이제는 참조자를 이용해서 만들수 있단 말이죠.
다음예제를 보면서 알아보도록 하겠습니다.



Swap1() 는 Call by value 에 의한 두 숫자의 값을 바꾸는것.
Swap2() 는 Call by reference에 의한 두 숫자의 값을 바꾸는것. (포인터사용)
Swap3() 는 Call by reference에 의한 두 숫자의 값을 바꾸는것. (참조자 사용)
이렇게 정의가 되어있습니다. 

swap1() 함수를 사용했을때는 값을 매개변수로 전달했기 때문에 함수 외부에 선언된 변수에 접근이 불가능 하므로 값이 바뀌지 않는 결과가 나타나게됩니다. 하지만 swap2() 함수를 사용했을때는 포인터를 사용하여 주소값을 매개변수로 전달했기 때문에 함수 외부에 선언된 변수에 접근이 가능하여 값이 바뀌는 결과가 나타납니다.
swap3() 함수는 매개변수를 참조자로 선언하였습니다. 참조의 대상이 될 변수를 인자로 전달하게되면 되겠군요! 그렇게 val1과 val2를 전달한뒤 참조자를 통해서 함수내부에서 값을 교환하였더니 서로 바뀐숫자값이 출력이 되었습니다. 도대체 어떻게 이렇게 될까요? 바로참조자를 이용하면 함수 내부에서도 외부에 선언된 변수에 접근이 가능하기 때문입니다. 

(참조자를 쓰면 포인터는 사용안해도 되겠네요? 라고 혹시 생각하는 분이 있겠지만.. 그건 아닙니다. 앞으로 코딩을 하는데 상황에 알맞게 참조자와 포인터 둘다 빈번히 사용하는게 알맞습니다.)

매개변수 부분에 int &a, int &b 로 선언만 되어있고 변수값 참조를 하지않는데 어떻게 swap3() 함수가 에러없이 정의가 가능한지 궁금한 분들이 있으실겁니다. 정확하게 참조자를 이해하고 공부하신분 입니다. 하지만 매개변수는 함수가 호출되었을때 전달되는 인자로 초기화가 되는것이기 때문에 아무 문제가 없습니다. 다시말해서 swap3()함수는 매개변수에 인자가 전달되지 않으면 함수가 실행이안되고 인자가 전달되면 그 순간 선언과 초기화가 이루어져서 함수가 실행된다는 의미랍니다. 

(참조자 팁 <- 여기서 반환형의 참조형(&)인 함수에 대해 알아두시기 바랍니다. 따로 해당 글에서는 따로 정리를 안하도록 하겠습니다.)

const를 이용한 참조자의 선언.
C언어에서 배웠던 const(상수) 한정자에 대한 개념을 간단히 집고 넘어가겠습니다.

*상수는 한번 선언후 초기화를 하면 절대 변하지 않으며 변할 수도 없다.

여기서 const는 참조자는 상수(number)를 참조 할 수 있게 해주는 역할을 합니다. 어떻게 그것이 가능할까요? 다음 예제를 통해 알아보도록 하겠습니다.




const 한정자를통해 int &a = 10으로 초기화 시켰습니다. 이때 저장되는 숫자 10은 바로 "리터럴" 입니다. (리터럴이 생각이 안나면 => 리터럴)
그러면 cont int &a = 10; 다음줄로 넘어가게되면 '10'이라는 리터럴값은 메모리공간속에서 사라지게 됩니다. 그러면 참조자 a는 아무것도 참조할것이 없게되므로 에러가 발생하게되는것이 정상입니다. 그러나 위의 코드는 아무런 에러를 보여주지 않고있습니다. '10'이라는 값이 메모리상에 계속 남아있기 때문에 참조자 a가 참조를 잘하고 있다는 소리겠지요?
여기에 특수한 로직이 하나숨어져있습니다. const 한정자를 이용해서 리터럴을 참조할 때는 메모리에 '임시변수'를 하나 만들어 그곳에 '10'을 저장하여 참조자가 문제없이 참조할 수 있도록 해준답니다. 
또 참조자 a는 const로 선언되어있으므로 절대 값이 바뀔수 없습니다.

이렇게 리터럴을 참조하는 방식을 가지고 더욱 직관적인 함수를 만들어낼 수 있습니다.
아래 예제를 보도록하겠습니다.



addnum1()과 addnum2() 두 함수를 사용할때 차이점 보이시나요?
addnum2() 함수처럼 매개변수 인자를 리터럴로 받아오게 되면 addnum1() 함수를
사용할때 처럼 굳이 val1과 val2를 선언해서 인자전달을 하는 번거로움이 줄어들게
된답니다.

이렇게 자료형 bool과 참조자에대해서 알아보는 시간이 모두 끝났습니다.
다들 쉽게쉽게 이해 되셨길바라면서 강의를 마치겠습니다. 다음강에서 뵙겠습니다.
(궁금한점은 댓글 달아주시면 답변드리겠습니다.)








댓글 없음:

댓글 쓰기