C++ 프로그램의 함수
C++ 표준 라이브러리(함수와 클래스)
써드 파티 라이브러리(함수와 클래스)
직접 구현한 함수와 클래스
함수 → 모듈화 → 재사용성
코드를 독립적인 연산으로 분할
연산들을 재사용
int main(){
read_input();
process_input();
write_output();
return 0;
}
큰 업무를 작은 단위의 업무(함수)로 분할
함수의 기능, 필요로 하는 정보, 리턴하는 것, 어떤 오류가 발생하는지, 성능상의 제약에 대해 이해해야 함
함수의 정의에 필요한 요소
1. 이름
…함수의 이름
…변수의 명명 규칙과 동일
… 의미가 있는 이름이어야 한다
2. 매개변수 리스트
…함수에 전달되는 값(인자)들
…타입이 명시되어야 함
3 리턴 타입
…연산 결과의 반환 타입
4 본문(body)
…함수가 호출되었을 때 실행되는 명령문
…대괄호(”{ }”) 내부
//함수의 정의
int func_name(int a){
statements;
return 0;
}
//함수의 호출
int main(){
for(int i = 0; i <10; i++){
func_name();
}
reutnr 0;
}
함수 프로토타입
컴파일러는 함수의 호출 이전에 함수의 정의를 알아야 함
함수의 호출 이전에 함수를 정의해서 해결한다.
→작은 프로그램에서는 괜찮은 방법이다.
→일반적으로 효율적인 방법은 아니다.
함수 프로토타입을 사용
→함수의 전체 정의가 아닌 컴파일러가 알아야할 부분만을 미리 알려주는 개념
→전방 선언(forward declaration)이라고도 명칭
→프로그램의 초기에 위치
→헤더 파일(.h)의 활용
int func_name();
int main(){
func_name();
}
int func_name(){
statements;
return 0;
}
함수 매개변수 (Function Parameters)
함수를 호출할 때, 데이터를 전달 할 수 있다.
→함수의 호출에 있어서 전달하는 값을 인수(argument)라 함
→함수의 정의에 있어서 전달하는 값은 인자 또는 매개 변수(parameter)라고 함
인수와 매개변수는 개수, 순서와 타입이 일치해야 함
int add_n(int ,int); //prototype
int main(){
int result = 0;
result = add_n(100, 200); // function call with argument
return 0;
}
int add_n(int a, int b){
return a + b;
}
값 전달 (Pass-by-value)
함수에 데이터를 전달할 때는 값으로 전달(pass-by-value)됨
...데이터 값이 복사되어 전달
...전달된 인수는 함수를 통해 변화되지 않음
→ 실수로 값이 변화하는 것을 방지
→ 값을 변화시키는 것이 필요하거나, 복사 비용이 높을 때를 위한 방법 존재(포인터 / 참조자)
형식 매개변수(formal parameter) : 함수 선언의 매개변수
실제 매개변수(actual parameter) : 함수 호출시 실제로 사용되는 인자
void param_test(int formal){
cout << formal << '\n'; // 50
formal = 100;
cout << formal << '\n; // 100
}
int main(){
int actual = 50;
cout << actual << '\n'; // 50
param_tesxt(actual);
cout << actual << '\n'; // 50
return문
반환(return)
→ void형 반환인 경우 return문 생략 가능
return문은 함수 내 어느 곳에서나 정의 가능
→ 마지막 줄에 정의하는 것이 의미적으로 명확함
return문을 통해 함수는 즉각적으로 종료
기본 인수(default argument)
함수의 선언에서 정의한 모든 매개변수가 전달되어야 함
기본 인수를 사용하면 인수가 주어지지 않을 시 기본값을 사용하도록 정의 가능
→ 동일한 값을 자주 사용할 경우
기본값은 함수 프로토타입 또는 정의부에 선언
→ 프로토타입에 선언하는 것이 기본
→ 둘다 선언해서는 안됨
여러개의 기본값을 사용할 경우 오른쪽부터 선언
double calc_cost(double base_cost,
double tax_rate = 0.06,
double shipping = 3.5);
int main(){
double cost = 0;
cost = calc_cost(100.0, 0.08, 4.5);
cost = calc_cost(100.0, 0.08);
cost = calc_cost(200.0);
return 0;
}
double calc_cost(double base_cost, double tax_rate, double shipping){
return base_cost +=(base cost * tax_rate) + shipping;
}
오버로딩 (Function Overloading)
서로 다른 매개변수 리스트를 갖는 동일한 이름의 함수 정의
추상화의 한 예
다형성의 한 예
→유사한 개념의 함수를 다른 타입에 대해 정의
객체지향 프로그램 구현을 위한 중요한 기법 중 하나
컴파일러는 주어진 인수와 함수들의 파라메터 정의를 기반으로 개별적인 함수를 구분할 수 있어야 한다.
int add_n(int, int);
double add_n(double, double);
int main(){
cout << add_n(10, 20) << '\n';
cout << add_n(10.0, 20.0) << '\n';
return 0;
}
int add_n(int a, int b){
return a + b;
}
double add_n(double a, double b){
return a + b;
}
반환 타입에 따르는 구분은 할 수 없다는 것 주의
int get_value();
double get_value();
cout << get_value() << '\n'; // error
함수 호출의 동작 방식
지역범위
블록{ } 내의 범위
함수의 매개변수는 함수 본체 내에서만 존재함
따라서 함수의 (복사된) 인자 및 지역 변수들은 함수의 실행 중에만 존재함
static 지역 변수
static 한정어를 사용해 예외 변수 지정 가능함
초기화가 필요하다.
void static_local_increment(){
static int n = 1;
cout << "num : " << n << '\n';
n++;
cout << "num : " << n << '\n';
}
int main(){
static_local_increment(); // 1 2
static_local_increment(); // 2 3
static_local_increment(); // 3 4
전역 범위
함수 밖에 정의된 변수는 어디서나 접근 가능
전역 변수는 사용하지 않는 것이 좋음
→전역 상수는 OK
함수 호출의 동작방식
Function call stack
LIFO(Last in first out)
Stack Frame(Activation Record)
→ 함수의 호출이 발생할 때마다 구분선이 생성됨
→ 함수의 지역 변수와 매개변수는 그 구분선 내에 생성됨
→ 함수의 호출이 끝나면 구분선 내의 메모리가 자동으로 해제됨
스택은 유한하고, stack overflow 발생할 수 있다.
스택 메모리
코드 공간
Static 변수, 전역 변수, String 리터럴
힙(Heap) : 힙 메모리는 크지만 느림, 메모리를 직접 해제해주어야함
스택(Stack) : 스택 메모리는 빠르지만 작음, 메모리가 자동으로 해제됨
int func2(int x, int y, in z){
x += y + z;
return x;
}
int func1(int a, int b){
int result;
result = a + b;
result = func2(result, a, b);
return result;
}
int main(){ //int result 0x
int x = 10; //int z 0x
int y = 20; //int y 0x
int z; //int x 0x
z = func1(x, y);
cout << z << '\n';
return 0;
}
Debug - Window- Call Stack에서 스택 구분선 확인 가능
배열의 전달과 pass-by-reference
배열을 함수로 전달
형식적 매개변수 정의에 “[ ]”를 사용하여 배열을 전달 가능
주의) 배열의 요소들은 복사되지 않는다
배열의 이름은 배열의 첫 요소의 메모리를 가리킴
→ 실제 매개변수에 복사되는 것은 이 메모리
따라서, 배열에 얼마나 많은 요소가 저장되어 있는지 함수는 알지 못함
void print_a(const int numbers[], int size);
void zero_a(int numbers[], int size(){
for(int i = 0 ; i < size; i++){
numbers[i] = 0;
}
}
int main(){
int my_n[]{1,2,3,4,5};
print_a(my_n, 5);
zero_a(my_n, 5);
print(my_n, 5); // 0 0 0 0 0
return 0;
}
void print_a(const int numbers[], int size){
for(int i = 0 ; i < size; i++){
cout << numbers[i] << '\n';
}
}
변화를 방지하는 안전한 코드를 위해 const 키워드 사용가능
→ const int numbers[]
참조자로 전달 (Pass-by-reference)
함수 내에서 값의 변환이 필요할 경우 사용
값의 변환을 위해서는 매개변수의 주소값이 필요
배열이 아닌 경우에도 C++에서는 참조자(reference)를 통해 가능
형식 매개변수를 실제 매개변수의 별명처럼 사용하는 개념
//참조자 & 기호 사용
void scale_n(int& n);
int main(){
int n = 1000;
scale_n(n);
cout << n << '\n';
return 0;
}
void scale_n(int& n){
if(n > 100){
n = 100;
}
}
//참조자 swap 예제
void swap(int& a, int& b)
int main(){
int x = 10, y = 20;
cout << x << ' ' << y << '\n'; // 10 20
swap(x, y);
cout << x << ' ' << y << '\n'; // 20 10
return 0;
}
void swap(int& a, int& b){
int tmp = a;
a = b;
b = tmp;
}
참조자를 사용하지 않는 경우에는 print를 위해 메모리 2배 사용
참조자를 사용하되, 값의 변경이 필요 없을 경우에는 const로 안정성 확보
void print(const vector<int> &v);
int main(){
vector<int> v{1, 2, 3, 4, 5};
print(v);
return 0;
}
void print(const vector<int> &v){
for(auto n : v){
cout << n << '\n';
}
}
inline함수
함수의 호출에는 어느 정도 오버헤드가 존재
→ Activation stack 생성, 파라메터 처리, pop stack, 리턴 값 처리등
함수를 inline으로 정의하면 컴파일 단계에서 함수내의 명령문으로 함수 호출이 대체
→ 일반적인 함수 호출보다 빠름
→ 바이너리 파일의 용량이 커질수 있다
→ 컴파일러 내부적으로 알아서 inline으로 처리하기도 함
inline int add_numbers(int a, int b){
return a + b;
}
int main(){
int result;
result = add_numbers(100, 200); //함수 본문인 100 + 200으로 대체
return 0;
}
재귀함수(recursive)
스스로를 호출하는 함수
Factorial
→ 재귀 호출을 끝내는 base case가 반드시 실행되어야 한다. (stack overflow 주의)
unsinged long long factorial(unsigned long long n){
if(n == 0){ // base case
return 1;
}
return n * factorial(n - 1);
}
int main(){
cout << factorial(5) << '\n';
return 0;
}
Multiple return values
참조자 / 포인터를 인자로 전달
인자의 전달에 복사가 발생하지 않아 효율적
같은 타입인 경우 (array, vector등)을 활용
std::tuple활용
std::make_pair(), std::get()
struct / class 활용
Memory layout additions
release 모드에서 컴파일하면 지역변수들이 연속된 메모리 공간에 할당되나, 디버그 모드에서 컴파일 하면 중간중간에 공백이 존재
'Language > C++' 카테고리의 다른 글
06.참조자 (0) | 2023.12.22 |
---|---|
05.포인터 (0) | 2023.12.22 |
03.basic-syntax (1) | 2023.12.22 |
02. 변수와 상수 (1) | 2023.12.22 |
01. C++ 프로그램의 구조 (0) | 2023.12.21 |
댓글