본문 바로가기
코스웨어/16년 스마트컨트롤러

2016-09-21_조재찬_스터디일지_C++기초 3 (bool, reference, new&delete, C 표준함수 호출)

by 알 수 없는 사용자 2016. 9. 21.
728x90
반응형

새로운 자료형 bool


c++에서 true와 false는 참,거짓 의미하는 1바이트 data

#include <iostream>
using namespace std;

int main(void)
{
	int num = 10;
	int i = 0;

	cout << "true: " << true << endl;	// true는 참을 의미하는 1바이트 data지만 정수로 변환되어 1 출력
	cout << "false: " << false << endl;	// 정수로 변환되어 0 출력

	while (true)
	{
		cout << i++ << ' ';
		if (i>num)
			break;
	}
	cout << endl;

	cout << "sizeof 1: " << sizeof(1) << endl;			// 정수 1, 0과는 다름 개념임을 보여줌
	cout << "sizeof 0: " << sizeof(0) << endl;
	cout << "sizeof true: " << sizeof(true) << endl;	// true와 false는 참,거짓 의미하는 1바이트 data
	cout << "sizeof false: " << sizeof(false) << endl;
	return 0;
}


Output:

1
2
3
4
5
6
7
true: true
false: false
0 1 2 3 4 5 6 7 8 9 10 
sizeof 1: 4
sizeof 0: 4
sizeof true: 1
sizeof false: 1



true와 false는 bool type data

bool type 변수는 true와 false정보를 저장 가능



#include <iostream>
using namespace std;

int main()
{
	int num;
	bool height;

	cout << "당신의 키 입력(cm) : ";
	cin >> num;

	if (num > 180) height = true;
	else height = false;

	cout << boolalpha;	// bool 변수의 값을 1과 0 대신에 true와 false로 출력하도록 함
	cout << "키가 180이 넘는가? " << height << endl;

	return 0;
}


Output :

당신의 키 입력(cm) : 175

키가 180이 넘는가? false



Reference (참조자)

변수는 할당된 메모리 공간에 붙여진 이름이며, 그 이름을 통해 해당 메모리 공간에 접근이 가능


int num1=2010;


위의 변수 선언으로 2010으로 초기화된 메모리 공간에 num1이라는 이름이 붙게 됨


int &num2=num1;


위에서 num2앞에 붙은 &연산자는 참조자의 선언을 뜻하며, 변수 num1에 대한 참조자 num2를 선언해라는 의미.


이 결과, num1이라는 이름이 붙은 메모리 공간에 num2이라는 이름이 더 하나 붙게 됨


num2=3047;


위 문장으로 인해 num1, num2라는 이름이 붙은 메모리 공간에 3047이 저장됨


- 관련 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

int main()
{
	int num = 10;
	int &ref = num;

	ref = 20;
	cout << "val : " << num << endl;
	cout << "ref : " << ref << endl;

	cout << "val : " << &num << endl;
	cout << "ref: " << &ref << endl;

	return 0;
}


Output:

1
2
3
4
val : 20
ref : 20
val : 0xfff84d48
ref: 0xfff84d48


변수 num과 참조자 ref가 동일한 메모리 공간을 참조하게 되며 주소값도 같음을 알 수 있다.


참조자의 수에는 제한이 없으며, 참조자를 대상으로 참조자를 선언하는 것도 가능



참조자의 선언 가능 범위


참조자는 변수에 대해서만 선언 가능. (배열이 아닌 배열요소도 변수로 간주)

선언과 동시에 누군가를 참조해야 함.


int &ref=20;    (x)

상수를 대상으로 참조자 선언 불가


int &ref;    (x)

미리 참조자를 선언후, 후에 누군가를 참조하는 것도 불가


int &ref=NULL;    (x)

NULL 초기화 불가



배열 요소(배열이 아님)를 대상으로 참조자 선언 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;

int main()
{
	int arr[3] = { 1,2,3 };
	int &ref1 = arr[0];
	int &ref2 = arr[1];
	int &ref3 = arr[2];

	cout << ref1 << endl;
	cout << ref2 << endl;
	cout << ref3 << endl;

	return 0;
}


Output:

1
2
3
1
2
3


다음과 같이 포인터 변수를 대상으로 참조자 선언 가능

#include <iostream>
using namespace std;

int main()
{
	int num = 12;
	int * ptr = &num;
	int ** dptr = &ptr;

	int &ref = num;
	int * (&pref) = ptr;
	int ** (&dpref) = dptr;

	cout << ref << endl;
	cout << * pref << endl;
	cout << ** dpref << endl;

	return 0;
}


Output:

1
2
3
12
12
12



다음의 함수 정의는 Call-by-value도 될수 있고 Call-by-reference도 될 수 있다.

int * SimpleFunc(int * ptr)

{

. . . .

}


다음과 같이 정의되면 Call-by-value

int * SimpleFunc(int * ptr)

{

return ptr+1;

}


주소 값이 전달되었냐가 아닌, 주소값이 참조의 도구로 사용되었냐가 구분의 기준이 됨


c++에서는 주소값을 이용한 Call-by-reference외에도 (혹자는 이를 Call-by-address라 부르기도 한다)

참조자를 이용한 Call-by-reference도 존재한다.



참조자를 이용한 Call-by-reference


#include <iostream>
using namespace std;

void SwapByRef2(int &ref1, int &ref2)
{
	int temp = ref1;
	ref1 = ref2;
	ref2 = temp;
}

int main()
{
	int val1 = 10;
	int val2 = 20;

	SwapByRef2(val1, val2);
	cout << "val 1 : " << val1 << endl;
	cout << "val 2 : " << val2 << endl;

	return 0;
}


Output:

1
2
val 1 : 20
val 2 : 10


참조자를 이용해 값을 증가시키는 함수, 부호를 바꾸는 함수 정의하고

각 함수의 호출결과 확인

#include <iostream>
using namespace std;

void Func(int &ref)
{
	ref++;
}

void Func2(int &ref)
{
	ref = ref*(-1);
}

int main()
{
	int val1 = 10;
	Func(val1);		
	cout << "val 1 : " << val1 << endl;

	Func2(val1);
	cout << "val 1 : " << val1 << endl;
	
	return 0;
}


Output:

1
2
val 1 : 11
val 1 : -11



포인터 변수 ptr1, ptr2가 가리키는 대상이 바뀌도록 하는 SwapPointer 함수 정의하고 결과를 출력해 확인

#include <iostream>
using namespace std;

void SwapPointer(int * (&ptr1), int *(&ptr2))
{
	int * temp = ptr1;
	ptr1 = ptr2;
	ptr2 = temp;
}

int main()
{
	int val1 = 10;
	int val2 = 20;

	int * ptr1 = &val1;
	int * ptr2 = &val2;

	SwapPointer(ptr1, ptr2);
	
	cout << "ptr 1 -> " << *ptr1 << endl;		
	cout << "ptr 2 -> " << *ptr2 << endl;
	
	return 0;
}


Output:

1
2
ptr 1 -> 20
ptr 2 -> 10



참조자 기반 함수 정의의 단점


int num=24;

HappyFunc(num);

cout<<num<<endl;


C언어 관점에서 본다면 100% 24가 출력될거 같지만, C++에서는 얼마가 출력될지 알 수 없다.


void HappyFunc(int prm) { . . . .}      // 위와 같이 함수가 정의되어 있다면 24가 출력되겠지만

void HappyFunc(int &ref) { . . . . }    //  참조자가 매개변수에 있다면 num에 저장된 값을 변경할 수도 있다.


그렇기 때문에 참조자를 사용하는 경우 함수 원형을 확인해야하고, 참조자가 매개변수 선언에 있을 때

값의 변화가 일어나는지 함수의 몸체(body)까지 살펴볼 필요성이 생긴다.


이는 함수의 호출문장을 보고 함수 특성을 판단할 때 단점이 된다.

따라서 참조자의 활용이 쉬움에도 명확한 코드를 위해 포인터를 사용하는 경우도 있다.


다음과 같이 참조자 ref에 const 키워드를 이용하면 함수의 몸체까지 들여다볼 필요없이, 함수의 원형을 확인하는 정도로 그칠 수 있다.


void HappyFunc(const int &ref) { . . . . }    // 함수 HappyFunc내에서 참조자 ref를 이용한 값의 변경은 하지 않겠다는 const 선언



Return Type이 Reference Type인 경우


#include <iostream>
using namespace std;

int & RefRetFuncOne(int &ref)
{
	ref++;
	return ref;
}

int main()
{
	int num1 = 1;	
	int &num2 = RefRetFuncOne(num1);	// 함수호출 후 참조자를 반환하며, 이를 참조자 &num2에 저장
	
	num1++;		// 변수 num1과 참조자 num2의 값을 1씩 증가, 각각 3이 됨
	num2++;		// 마찬가지로 1씩 증가해 각각 4가 됨
	cout << "num1 : " << num1 << endl;
	cout << "num2 : " << num2 << endl;
		
	return 0;
}

Output:
1
2
num1 : 4
num2 : 4



바로 위 예제와 달리 일반 변수에 참조자 반환값 저장

#include <iostream>
using namespace std;

int & RefRetFuncOne(int &ref)
{
	ref++;
	return ref;
}

int main()
{
	int num1 = 1;	
	int num2 = RefRetFuncOne(num1);	// 함수호출 후 참조자를 반환하지만 참조자가 아닌 일반변수를 선언하고 반환값을 저장
	
	num1+=1;	
	num2+=100;	
	cout << "num1 : " << num1 << endl;
	cout << "num2 : " << num2 << endl;
		
	return 0;
}


Output:

1
2
num1 : 3
num2 : 102


반환형이 참조형인 경우

: 위의 두 예제와 같이 반환대는 대상을 참조자로 받거나 변수로 받을 수 있다.



반환형이 참조자가 아닌 경우

: ref를 반환하든, 변수에 저장된 값을 반환하든 차이는 없다.


int RefRetFuncTwo(int &ref) // 반환형이 참조형( int & )은 아님

{

ref++;

return ref;

}


반환형이 값의 형태라면 참조자로 그 값을 받을 수 없다.

(상수를 참조하게 되므로)


int num2=RefRetFuncTwo(num1); (o)

int &num2=RefRetFuncTwo(num1); (x)




아래와 같이 지역변수를 참조의 형태로 반환하는 것은 문제가 된다.


int & RetuRefFunc(int n)

{

int num=20;

num+=n;

return num; // 지역변수의 반환

}


int &ref = RetuRefFunc(10); // ?? ref가 참조하는 대상이 소멸



const 참조자의 잘못된 사용


const int num=20;

int &ref=num;

ref+=10;

cout<<num<<endl;


위에서 num을 const선언해놓고 참조자를 통해 값을 변경하는 것은 const 선언의 이유를 잃게 하므로 에러



따라서 const 참조자는 프로그램의 안정성을 위해 다음과 같이 쓰는것이 좋다.


const int num=20;

const int &ref=num; // ref로 참조는 하되 값을 변경하지 않겠단 의미

const int &ref=50; // const참조자를 통해 상수 참조 가능



const참조자가 상수를 참조가능한 이유

: 리터럴 상수는 참조자로는 참조할 수 없게 되어 있다. 

하지만 const참조자로 참조할 경우에는 허용하며, 상수를 메모리 공간에 임시적으로 저장한다.


이러한 것이 가능하도록 한 이유는 아래와 같이 매개변수 형이 참조자인 경우에 상수를 전달할 수 있도록 하기 위함이다.


int Adder(const int &num1, const int &num2)

{

return num1 + num2;

}



malloc과 free함수를 대신하는 new와 delete


c언어에서의 malloc과 free 함수 

http://wowcat.tistory.com/3001


크기를 바이트 단위로 계산하지않고 type에 맞게 할당


int * ptr=new int;

double *arr=new arr[7];


이렇듯 new연산자로 할당된 메모리 공간은,

아래와 같이 delete함수 호출을 통해 소멸한다.


delete ptr; // 앞서 할당된 int형 변수 소멸

delete []arr; // 앞서 할당된 double형 배열 소멸



c에서 heap영역으로의 접근은 pointer를 통해서만 이루어졌지만 c++에서는 reference를 통한 접근도 가능.


참조자를 통해 heap에 접근하기


int * ptr = new int;

int & ref = *ptr; // heap영역에 할당된 변수에 대한 참조자 선언

ref=20;

cout<< * ptr << endl;



C++에서 C언어 표준함수 호출하기


c언어에 대응하는 c++ 헤더파일의 이름 규칙

: c를 더하고 .h를 빼면 된다.


C언어 헤더 C++ 헤더

#include <stdio.h> #include <cstdio>

#include <stdlib.h> #include <cstdlib>



c++의 표준함수는 std라는 namespace안에 선언되어 있음

또한 표준 C에 대응하는 표준 C++함수는 C++문법을 기반으로 변경 및 확장되었다 (함수 오버로딩을 통해 개선하는 등)


따라서, 가급적 C++에 표준함수를 호출하는 것이 좋다.


728x90