'참' 을 의미하는 의미하는 true 와 '거짓'을 의미하는 false
int main(void)
{
int num=10;
int i=0;
cout << "true: " << true << endl;
cout << "false: " << false << endl;
while (true)
{
cout << i++ << ' ';
if (i > num)
break ;
}
cout << endl;
cout << "sizeof 1: " << sizeof(1) << endl;
cout << "sizeof 0: " << sizeof(0) << endl;
cout << "sizeof true: " << sizeof(true) << endl;
cout << "sizeof false: " << sizeof(false) << endl;
return 0;
}
true: 1
false: 0
0 1 2 3 4 5 6 7 8 9 10
sizeof 1: 4
sizeof 0: 4
sizeof true: 1
sizeof false: 1
true는 ‘참’을 의미하는 1바이트 데이터이고, false는 ‘거짓’을 의미하는 1바이트 데이터이다.
이 둘은 각각 정수 1과 0이 아니다.
그러나 정수가 와야 할 위치에 오게 되면, 각각 1과 0으로 변환이 된다.
- int num1 = true; // num1 에는 1 이 저장됨
- int num2 = false; // num2 에는 0 이 저장됨
- int num3 = true + false // num3 = 1 + 0;
자료형 bool
true 와 false 는 bool 형 데이터이다.
true 와 false 정보를 저장할 수 있는 변수는 bool 형 변수이다.
bool isTrueOne=true;
bool isTrueTwo=false;
bool IsPositive(int num)
{
if (num < 0)
return (false);
else
return (true);
}
int main(void)
{
bool isPos;
int num;
cout << "Input num: ";
cin >> num;
isPos = IsPositive(num);
if (isPos)
cout << "Positive number" << endl;
else
cout << "Negative number" << endl;
return 0;
}
Input num: 12
Positive number
참조자(Reference)의 이해
참조 == alias
여기서 alias(참조) 에 대한 개인적 해석을 적어보자면
우리가 흔히 변수는 어떠한 메모리를 가리키는 것으로 이해할 수 있다.
우린 메모리를 직접 활용하는 게 아닌 해당 메모리의 심볼에 변수 이름을 지어줌으로 써 이 심볼을 통해 메모리에 접근을 하게 되는데
여기서 alias 란 동일한 메모리 주소를 가리키는 새로운 심볼 이름인 것이다.
한 마디로 같은 메모리 공간을 다른 이름으로 다룬다는 것이다.
변수 == 메모리 공간을 가리키는 것
참조 == 어떠한 변수와 동일한 메모리를 가리키는 것
포인터 == 메모리 공간을 가라키는 주소를 가리키는 것
참조자 관련 예제와 참조자의 선언
int main(void)
{
int num1 = 1020;
int &num2 = num1; // num2 는 num1 의 참조자이다.
// 따라서 이후부터 num1로 하는 모든 연산은 num2 로 하는 것과 동일한 결과를 보인다.
num2=3047;
cout << "VAL: " << num1 << endl;
cout << "REF: " << num2 << endl;
cout << "VAL: " << &num1 << endl;
cout << "REF: " << &num2 << endl;
return 0;
}
VAL: 3047
REF: 3047
VAL: 0012FF60
REF: 0012FF60
참조자의 선언 가능 범위
int main(void)
{
int arr[3] = {1, 3, 5};
int &ref1 = arr[0];
int &ref2 = arr[1];
int &ref3 = arr[2]; // 변수의 성향을 지니는 대상이라면 참조자의 선언이 가능
// 배열의 요소 역시 변수의 성향을 지니기 때문에 참조자의 선언이 가능
cout << ref1 << endl;
cout << ref2 << endl;
cout << ref3 << endl;
return 0
}
1
3
5
포인터 변수 대상의 참조자 선언
int main(void)
{
int num = 12;
int *ptr = #
int **dptr = &ptr;
int &ref = num;
int *(&pref) = ptr;
int **(&dpref) = dptr;
cout << ref << endl; // ptr 과 dptr 역시 변수, 다만 주소 값을 저장하는 포인터 변수일 뿐이다.
cout << *pref << endl; // 따라서 이렇듯 참조자의 선언이 가능하다.
cout << **dpref << dptr;
return 0;
}
12
12
12
Call-by-value & Call-by-reference
void SwapByValue(int num1, int num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
}
// 값을 전달하면서 호출하게 되는 함수이므로 이 함수는 Call-by-value이다.
// 이 경우 함수 외에 선언된 변수에는 접근이 불가능하다.
void SwapByValue(int *ptr1, int *ptr2)
{
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
}
// 값은 값이되, 주소 값을 전달하면서 호출하게 되는 함수이므로 이 함수는 Call-by-reference 이다.
// 이 경우 인자로 전달된 주소의 메모리 공간에 접근이 가능하다.
Call-by-address? Call-by-reference!
C++에는 두 가지 형태의 Call-by-reference가 존재한다.
하나는 주소 값을 이용하는 형태이며,
다른 하나는 참조자를 이용하는 형태이다.
참조자를 이용한 Call-by-reference
void SwapByRef(int &ref1, int &ref2) // & 참조자 기반 Call-by-reference
{
int temp = ref1;
ref1 = ref2;
ref2 = temp;
} // Call-by-reference
int main(void)
{
int val1 = 10;
int val2 = 20;
SwapByRef(val1, val2);
cout << "val1: " << val1 << endl;
cout << "val2: " << val2 << endl;
return 0
}
매개변수는 함수가 호출될 때 선언이 되는 변수이므로, 함수 호출의 과정에서 선언과 동시에 전달되는 대상으로 초기화된다.
즉, 매개변수에 선언된 참조자는 여전히 선언과 동시에 초기화된다.
const 참조자
함수 파라미터 작성시 단순 참조자를 설정해두면, 호출자 입장에선 전달한 인자의 값의 변경여부를 알 수 없다.
확인을 하고자 한다면 해당 함수의 구조를 직접 살펴볼 수 밖에 없는 수고가 발생한다.
이를 해결하고자 사용하는 것이 const 참조자
함수 HappyFunc 내에서 참조자 'ref를 이용한 값의 변경은 허용하지 않겠다' 라는 의미로 사용되어,
굳이 함수 구조를 훑어보는 수고를 덜어줄 수 있게 되었다.
함수 내에서 참조자를 통한 값의 변경을 진행하지 않을 경우 참조자를 const로 선언하여 사용하면 다음과 같은 장점을 얻을 수 있다.
- 함수의 원형 선언만 봐도 값의 변경이 일어나지 않음을 판단할 수 있다.
- 실수로 인한 값의 변경이 일어나지 않는다.
반환형이 참조이고, 반환도 참조로 받는 경우
int &RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num1 = 1;
int &num2 = RefRetFuncOne(num1);
num1++;
num2++;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
return 0;
}
num1: 4
num2: 4
동일 메모리 공유 과정 변천사는 다음과 같다
num1(초기화) | num1, ref (함수 호출) |
num1, ref (ref++) |
num1, num2 (함수 반환) |
num1, num2 (num1++) |
num1, num2 (num2++) |
반환형은 참조이되 반환은 변수로 받는 경우
int &RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num1 = 1;
int num2 = RefRetFuncOne(num1); // 컴파일러에서 참조자의 값을 복사해서 num2에 저장
num1 += 1;
num2 += 100;
cout << "num1: " << num1 << endl;
cout << "num2: " << num2 << endl;
return 0;
}
num1: 3
num2: 102
이는 반환이 참조형이더라도 반환받는 변수가 참조자가 아니기 때문에 컴파일러에서 반환되는 참조자의 값을 복사해서 반환받는 변수에다가 저장을함. 이 경우는 num2 와의 메모리 공유가 일어나지 않음
참조를 대상으로 값을 반환하는 경우
// 함수 반환이 참조형일 경우
int &RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num2 = RefRetFuncOne(num1); // 유효
int &num2 = RefRetFuncOne(num1); // 유효
...
}
----------------------------------------------------------------
// 함수 반환이 값일 경우
int RefRetFuncOne(int &ref)
{
ref++;
return ref;
}
int main(void)
{
int num2 = RefRetFuncOne(num1); // 유효
int &num2 = RefRetFuncOne(num1); // 무효!!
...
}
반환형이 참조형인 경우에는 반환되는 대상을 참조자로 그리고 변수로 받을 수 있다.
그러나 반환형이 값의 형태라면, 참조자로 그 값을 받을 수 없다
잘못된 참조의 반환
int &RefRetFunc(int n)
{
int num = 20;
num += n;
return num; // 함수의 num 은 해당 함수의 지역 변수로써,
}
int main(void)
{
int &ref=RefRetFunc(10); // 해당 함수가 닫히면 참조대상도 같이 소멸되기 때문에 에러를 발생시킴
...
}
const 참조자의 또 다른 특징
const int num = 20; // num 을 const 로 선언했는데 참조자가 const 가 아닌 것이 유효해버리면
int &ref = num; // num 의 값 또한 변경이 가능해져 const 의 의미가 없어지므로 컴파일 과정에서 에러 처리를 한다.
ref += 10;
----------------------------------------------
const int num = 20;
int &ref = num;
ref += 10;
// 따라서 한번 const 선언이 들어가기 시작하면 관련해서 몇몇 변수들이 const 로 선언되어야 하는데,
// 이는 프로그램의 안전성을 높이는 결과로 이어지기 때문에, const 선언을 빈번히 하는 것은 좋은 습관이라 할 수 있다.
참조자의 상수 참조
const int &ref = 30 // 30 은 상수
const 참조자는 상수를 참조할 수 있다.
이유는 상수를 const 참조자로 참조할 경우, 상수를 메모리 공간에 임시적으로 저장하기 때문
상수 참조를 유효한 기능으로 설정된 이유는
int Adder(const &num1, const &num2)
{
return num1 + num2;
}
함수의 매개변수형이 참조자일 경우 상수도 전달될 수 있도록 하기위한 초지
new & delete
malloc 을 대신하는 C++ 의 메모리 동적 할당 방법.
크기를 바이트 단위로 계산하는 일을 거치지 않아도 된다.
int *ptr1 = new int; // int 형 변수의 할당 --동적 메모리 변수는 실사용이 흔치 않다
double *ptr2 = new double; // double 형 변수의 할당 --동적 메모리 변수는 실사용이 흔치 않다
int *arr1 = new int[3]; // 길이가 3인 int 형 배열의 할당
double *arr32 = new double[7]; // 길이가 7인 double 형 배열의 할당
free 대신하는 C++ 의 메모리 해제방법
delete ptr1;
delete ptr2;
delete []arr1; // 배열 해제시 [] 필수
delete []arr2; // 배열 해제시 [] 필수
new 연산자로 할당된 메모리 공간은 반드시 delete 함수호출을 통해서 소멸해야 한다.
특히 이후의 객체의 생성 및 소멸 과정에서 호출하게 되는 new & delete 연산자의 연산자의 연산특성은 malloc & free와 큰 차이가 있다!
포인터를 사용하지 않고 힙에 접근하기
int *ptr = new int;
int &ref = *ptr; // 힙 영역에 할당된 변수에 대한 참조자 선언
ref = 20;
cout << *ptr << endl; // 출력 결과는 20
변수의 성향을 지니는(값의 변경이 가능한) 대상에 대해서는 참조자의 선언이 가능
C언어의 경우 힙 영역으로의 접근을 위해서는 반드시 포인터를 사용해야만 했다.
하지만 C++에서는 참조자를 이용한 접근도 가능하다!
C++ 의 C 표준 헤더 사용 : C 추가 .h 제거
#include <stdio.h> -> #include <cstdio>
#include <stdlib.h> -> #include <cstdlib>
#inlcude <math.h> -> #include <cmath>
#inlcude <string.h> -> #include <cstring>
C 의 표준 헤더를 그대로 가져와서 C++ 스타일로 래핑한 것
C 의 함수를 C++ 스타일로 래핑한 덕분에
'정수형만' 사용하는 C의 abs 함수를 C++ 에선 '여러 자료형'으로 사용할 수 있게 되었음
'C++ > 윤성우의 열혈 프로그래밍 C++' 카테고리의 다른 글
윤성우의 열혈 C++ chpt6. (2) | 2025.06.08 |
---|---|
윤성우의 열혈 C++ chpt5. (0) | 2025.05.16 |
윤성우의 열혈 C++ chpt4. (0) | 2025.05.12 |
윤성우의 열혈 C++ chpt2. (0) | 2025.05.11 |
윤성우의 열혈 C++ chpt1. (0) | 2025.05.11 |