오늘의 TIL 목차 (22.08. 02)
- 함수 (feat. return;)
- 함수 오버로딩
- 재귀함수
- 전역 변수, 정적 변수 (static)
함수
[ 함수 ]
: 기능을 코드 형태로 만들어 놓은 문법, 코드의 재사용성을 위해 사용
void main (void);
반환타입 함수명 매개변수/인자값/파라미터
■ 함수의 종류
1. 입력값 X , 반환값 X 2. 입력값 O, 반환값 X
3. 입력값 X, 반환값 O 4. 입력값 O, 반환값 O
void One(void); // 1. 입력값이 없고 반환값도 없는 경우
void Two(int _iNumber); // 2. 입력값은 있는데 반환값은 없는 경우
int Three(void); // 3. 입력값은 없는데 반환값(int형)은 있는 경우
float Four(char _c); // 4. 입력값도 있고 반환값(float형)도 있는 경우
→ void 는 빈, 없는 을 의미함
■ 기능 / 주의
- 함수 안에서 함수를 호출할 수 있다. (함수의 반환값에 따라서 무한루프 돌 가능성이 높으므로 판단을 잘 해야함)
- 함수의 매개변수에 함수가 들어갈 수 있다.
- 함수 선언부와 정의부를 분류하여 입력하는 습관을 들여야 한다.
- 함수를 선언 / 정의 할 때 매개변수가 있을 경우 자료형을 무조건 명시해줘야 한다.
- 반환값이 있을 경우 무조건 return을 해줘야하며 return 값은 반환 타입과 동일해야 한다.
- 함수의 매개변수는 함수 안에서 선언된 지역 변수이므로 함수가 종료되면 해당 지역 변수는 소멸된다.
- 매개변수는 '복사'해서 가져온 다음 함수가 종료되면 소멸되기 전 '복사'해서 넘겨주고 소멸된다. (call by value)
- 복사가 많이 이뤄질수록 속도가 느려지므로 매개변수 사용량이 늘어날수록 속도가 줄어든다.
■ 예시
// 매개 변수에 값을 넣는 행위는 대입이 아니라 복사를 하는 행위 - call by value
// 함수 선언부
void Draw(void);
int Rand(unsigned int _iRand);
void Print_Number(int _iNumber);
void main(void)
{
Draw(); // "글자 출력" 출력하고 g_iNumber에 50000가 대입됨
cout << g_iNumber << endl; // 50000
함수의 매개변수에 함수 넣기 (매개변수에 식 넣어서 적용 가능)
Print_Number(g_iNumber-10000); // 50000-10000인 40000 출력하고 +100 적용됨
함수간의 덧셈식
cout << "난수를 사용하여 매개변수에 덧셈식 넣기" << endl;
cout << (g_iNumber + Rand(rand())); << endl; // 40100에 1~5 범위의 난수 더해짐
g_iNumber += Rand(rand()); // 윗줄과 다른 난수 더해짐 (당연히 난수니까 다음 순서 난수 추출)
cout << g_iNumber << endl; // 윗윗줄은 덧셈식을 출력했을 뿐 대입한 건 아니므로
// g_iNumber += Rand(rand()); 는 기존 40100 에서 난수 더해진 값 출력
}
// 함수의 정의부
int Rand(unsigned int _iRand) // 수를 입력받아 나머지값 범위 1 ~ 5 를 출력하고 반환하는 함수
{
_iRand %= 5; // 난수 0 ~ 4 까지 출력
++_iRand; // ++를 해줌으로써 1 ~ 5까지 출력
cout << _iRand << endl; // 1~5 중 랜덤 출력
return _iRand; // 반환타입이 정수이므로 무조건 정수형으로 반환
}
void Draw(void) // g_iNumber에 50000을 대입하는 함수
{
// 함수의 몸체, 코드 블럭
cout << "글자 출력" << endl; // 함수를 정의하다, 구현하다
g_iNumber = 50000;
}
void Print_Number(int _iNumber) // 매개변수를 출력하고 100 더한 값을 g_iNumber에 대입하는 함수
{
cout << _iNumber << endl;
g_iNumber = _iNumber + 100;
}
→ 반환값이 없는 (void) 인 경우 cout << 함수 << endl; 불가능
[ return ]
: 함수에서 반환 / 함수 종료 기능을 하는 명령문 ( main 함수 포함 )
void main(void)
{
return; // main 함수 종료
}
■ 기능 / 주의
- return은 함수에서 값을 반환해줌과 동시에 해당 함수를 종료함 ( 값 반환 없이 종료만 가능)
- return은 아래 명령문을 실행하지 않고 즉시 해당 함수를 종료
- 함수에서 void가 아닌 반환타입이 있다면 무조건 동일한 반환타입으로 값을 return해줘야 함
■ 예시
int Plus (int _iA);
void main (void)
{
int iNum = 2;
cout << Plus(iNum) << endl; // 1
--Plus(iNum); // 함수는 반환값 있어도 증감 연산자 불가능
--iNum; // iNum == 1
if(0 == Plus(iNum)) // 조건식 (Plus(iNum))만 하면 0(거짓)으로 if문 실행 안됨
{
cout << Plus(++iNum) << endl; // --(++1)은 1 출력
return; // main 함수 종료
}
int Plus (int _iA)
{
cout << "Plus 함수 안 : " << _iA << endl; // _iA 출력
return --_iA; // --_iA를 복사해서 반환하고, 매개변수 _iA는 함수 종료와 동시에 소멸됨
cout << "return 아래 : " << _iA << endl; // return은 아래 실행문을 무시하고 함수를 종료하기에 출력 안됨
}
출력결과:
Plus 함수 안 : 2
1
Plus 함수 안 : 1
Plus 함수 안 : 2
1
→ 함수 매개변수의 반환 원리는 함수 종료 시 소멸되기 전 임시 저장 장치에 복사한 후 소멸, 임시 저장 장치는 반환 위치인 라인에서 값을 반환하고 해당 라인이 끝나면 소멸 (값이 소멸되지 않고 누적해서 저장하고 싶다면 static 사용)
: 함수에서 반환 / 함수 종료 기능을 하는 명령문 ( main 함수 포함 )
[ default 매개변수 ]
: 매개변수 값을 복사하지 않고 기본 값으로 사용하고자 할 때 쓰는 문법
void Num_Default(int _iA, float _fB = '0.f', char _cC = 'A');
void Num_Default(int _iA, float _fB, char _cC)
{
}
※ default 매개변수는 선언부에서만 정의할 것 ( 정의부 X )
■ 기능 / 주의
- default 매개변수는 반드시 오른쪽에서부터 초기화식을 수행해야한다.
- 매개변수가 있는 오버로딩 하는 함수가 있는지 확인해야함.
실행오류
// 1. 반드시 오른쪽에서부터 초기화해야됨
void Left_Default(int _iA = 10, float _fB = 3.f, char _cC);
// 2.둘 중 어떤 것을 출력해야 할지 몰라서 실행 오류
void Num_Default(int _iB, float _fC);
void Num_Default(int _iA, float _fB, char _cC = 'A');
Num_Default(50, 60.f); // default 매개변수를 출력할지 말지 기준이 모호해짐
■ 예시
void Num_Default(int _iA, float _fB, char _cC = 'A');
void main(void)
{
Num_Default(50, 60.f); // default 매개변수는 입력하지 않아도 디폴트 값으로 적용됨
cout << endl;
Num_Default(1, 24.f, C); // 디폴트 매개변수에 값을 입력하면 입력한 값으로 적용됨
}
void Num_Default(int _iA, float _fB, char _cC)
{
cout << _iA << "\t" << _fB << "\t" << _cC << endl;
출력결과:
50 60 A
1 24 C
함수 오버로딩
[ 함수 오버로딩 ]
: 같은 이름의 함수가 있을 때, 함수의 매개 변수에 따라 호출하는 함수가 결정되는 문법
void Show(void);
void Show(int _iNumber);
void Show(float _fNumber);
void Show(int _iNumber, int _iTest);
→ 함수 오버로딩은 같은 이름의 함수를 중복하여 정의하는 것을 의미
■ 기능 / 주의
1. 매개 변수의 종류에 따라 함수 호출이 결정
2. 매개 변수의 개수에 따라 함수 호출이 결정
3. 함수 오버로딩은 함수의 이름의 같고, 매개 변수에 따라서 동작원리가 결정되기 때문에 반환타입만 다른 경우에는 오버로딩 조건이 성립하지 않는다.
■ 예시
// 함수 선언부 생략
void main (void)
{
int iA = 0;
// 함수명이 같아도 매개변수 타입, 개수에 따라 해당하는 함수 출력
Show();
Show(50.f);
Show(5, 76.f);
Show(0.f, 15);
Show(iA);
}
void Show(void)
{
cout << "show1" << "\t";
}
void Show(int _iNumber)
{
cout << "show2" << "\t";
}
void Show(float _fNumber)
{
cout << "show3" << "\t";
}
void Show(float _fNumber, int _iNumber)
{
cout << "show4" << "\t";
}
void Show(int _iNumber, float _fNumber)
{
cout << "show5" << "\t";
}
출력결과:
show1 show3 show5 show4 show2
→ void Show(float _fNumber, int _iNumber)와 void Show(int _iNumber, float _fNumber)처럼 매개 변수의 종류와 갯수가 같아도 순서가 다르면 실행된다. 해당 순서에 맞는 매개변수의 함수를 호출
재귀함수
[ 재귀함수 ]
: 자기 자신을 호출하는 함수
void Return(int _iNumber)
{
if (0 >= _iNumber) // 함수 탈출문
return;
cout << "재귀 호출" << endl;
Return(--_iNumber); // 자기 자신 호출
}
■ 기능 / 주의
- 반드시 함수를 탈출 할 수 있는 조건을 삽입해야 한다. 그렇지 않으면 무한 루프에 빠질 위험이 높다.
- 매개 변수를 사용할 경우, call by value 타입으로만 지정해야 한다.
* call by value는 값에 의한 호출, 값은 포인터, 상수 ,, 를 제외한 char, int, float ,, 을 의미한다.
■ 예시
// 재귀함수를 이용한 팩토리얼 구하기 (n!)
int factorial(int _iNum)
{
int iResult = 0;
if (1 >= _iNum) // iNum이 1보다 작거나 같으면 함수 종료하는 조건
{
cout << _iNum << " = ";
return _iNum;
}
iResult = _iNum;
cout << _iNum << " * ";
iResult *= factorial(--_iNum);
/* _iNum *= factorial(--_iNum); 을 해버리면
--_iNum이 해당 라인에서 iNum 자체의 값을 바로 감소시키는 것이기 때문에
좌변의 iNum도 -- 된 채로 곱셈을 시작해서 (n-1) ! 이 출력된다.
ex) int iA = 0일 때,
cout << iA += ++ iA << end;
출력 결과 :
iA는 0 + 1이 아닌 해당 라인에서 iA 자체에 ++가 적용됐기 때문에 1 + 1인 2가 출력됨
*/
}
namespace
[ namespace ]
: 이름 충돌을 방지하기 위해 변수나 함수를 구분하는 소속 공간
namespace 소속명
{
몸체 코드
}
namespace 소속명
{
몸체 코드
}
■ 기능 / 주의
- 동일한 변수명, 함수명이어도 이름 공간으로 소속을 분리하면 이름 충돌 없이 사용 가능
[ 이름 공간을 사용하는 5가지 방법 중 일부 ]
1. using namespace 소속명;
- 전체 namespace 사용
* :: 지정 없이 이름 공간 안의 기능만 입력해도 사용 가능
* using namespace 소속명; 이 여러 개이고 해당 소속명에 같은 이름의 기능이 들어있을 경우 ::로 지정해서 사용해야함
2. 소속명 :: 소속 안 기능;
- 사용할 소속명과 해당 소속명 안의 기능을 지정해서 사용
■ 예시
// namespace는 공간 선언이 using보다 위에 와야함
namespace Test
{
void Function(void)
{
cout << "Test" << endl;
}
}
namespace Exam
{
void Function(void)
{
cout << "Exam" << endl;
}
}
using namespace Test;
using namespace Exam;
void main(void)
{
Test :: Function(); // 지정한 경우 using namespace 안해줘도 실행됨
/* 둘다 using 해준 경우 어느 namespace의 Function()함수를 써야할지 모르므로
namespace명 :: 함수; 로 명확히 해줘야 올바르게 실행 됨 */
}
출력결과:
Test
→ using namespace Test;만 해준 경우 Test :: 지정 없이 Function();만 사용해도 Test 안의 Function 함수가 실행됨
전역변수 & 정적변수
[ 정적변수 ]
: 선언된 함수 내에서만 사용 가능(지역변수 특성)하며 한 번만 초기화를 하고 프로그램이 종료될 때까지 메모리 공간에 존재(전역변수 특성) 하는 변수
static int iA; // 정적변수
■ 기능 / 주의
- 정적변수와 전역변수는 초기화를 하지 않아도 Data 영역의 Bss에서 자동 초기화 한 다음 Data 영역으로 넘어간다.
☞ 쓰레기값이 들어가는 것을 방지하기 위해 0으로 자동 초기화
- 함수 여러 번 호출해도 초기화는 단 한 번만 실행
☞ 함수 안 변수는 (매개변수 포함) 지역변수라 함수 종료 시 소멸되기 떄문에 함수 호출 시마다 변수 초기화되는 것을 방지하고 싶을 때 static 사용
■ 예시
'C++' 카테고리의 다른 글
| [TIL 9장] 이중 포인터 개념, 배열, 디버깅 (0) | 2022.08.04 |
|---|---|
| [TIL 8장] 포인터, 상수 포인터 (0) | 2022.08.03 |
| [TIL 6장] RAM 이론 (feat. 전역변수/지역변수) (0) | 2022.08.01 |
| [TIL 5장] 3항 연산자, for문(이중 for문) (0) | 2022.08.01 |
| [TIL 4장] 반복문(feat. break, continue), 난수, time(), typedef (0) | 2022.07.29 |