개요
자료구조 수업의 진도는 계속 나가는데 글을 쓰는 빈도는 점점 줄고 있습니다.
주기를 조금 줄여야겠네요.
오늘 쓸 함수에 대한 글은 지난주에 진작 나갔던 진도입니다... 아이고
저번주에 나간 게임 프로그래밍 시간에 나갔던 진도와 겹치는 부분이 있어서 합쳐서 정리하겠습니다.
함수란?
간단하게 함수는
input을 넣으면 output이 나오는 것
이라고 할 수 있습니다.
함수들은 선언할때 반환할 자료형, 이름과 매개변수(선택)가 필요합니다.
물론 반환값이 없는 void 함수도 존재합니다.
int max(int x, int y)
{
x > y ? return x : return y;
return x;
}
int 라는 반환형과 max라는 이름, 그리고 괄호에 들어간 매개변수를 통해 함수를 만들었습니다.
정말 신기하죠?
함수를 선언할때
C++은 객체지향 언어 주제에 절차지향적인 요소가 꽤 많습니다.
이런식으로 함수를 호출할때 main보다 밑에서 선언하고 구현하면 오류가 납니다.
#include<iostream>
using namespace std;
int main()
{
int a = 1;
a = func(a); // 컴파일 에러
cout << a;
}
int func(int a)
{
return a+1;
}
C++은 컴파일러가 코드를 위부터 아래로 하나하나 읽어오기 때문에
main을 읽는 도중에 아직 읽지 못한 func함수에 접근하지 못합니다.
근데 main위에 함수가 주렁주렁 달려있으면 조금 거추장스럽죠?
그래서 C++은 함수 전방선언 이란걸 지원합니다.
#include<iostream>
using namespace std;
int func(int a);
int main()
{
int a = 1;
a = func(a); // 컴파일 에러
cout << a;
}
int func(int a)
{
return a+1;
}
컴파일러에게 func라는 함수가 존재는 하니까 일단 봐주라고 빌면 컴파일러가 일단 봐줍니다.
물론 저렇게 안쓰고 그냥 func 함수의 본문을 main위로 끌어 올려도 됩니다만...
main 위쪽이 너무 길어지면 보기 안좋으니까 저렇게 선언만 하고 구현은 main 아래서 해주는 게 좋습니다.
Call by ~
함수에 들어가는 매개변수들은 3종류가 있습니다.
int func(int a) {} // Call by Value
int func(int* a) {} // Call by Address
int func(int& a) {} // Call by Reference
각각 Call by Value, Address, Referece라고 부르며
Value는 일반 값, Address는 주소 값, Reference는 참조형 변수를 넘겨줍니다.
Call by Value
int func(int a) {}
Call by Value는 값을 전달해줄때 값을 복사해서 전달해줍니다.
int func(int a)
{
a = 30;
return 0;
}
int main()
{
int a = 0;
func(a);
cout << a; // 출력 결과 : 0
}
그래서 이런식으로 값을 전달한 후 수정해도 바뀌는게 없습니다.
Call by Address
int func(int* a) {}
Call by Address는 매개변수로 포인터 변수를 넘깁니다.
포인터 변수란? 주소를 담는 변수이다.
일반적으로 함수 내에서 변수를 선언하면 스택 메모리에 할당되는데
이런식으로 주소와 값을 한꺼번에 저장합니다.
포인터 변수에 담긴 주소를 통해 저 값에 접근할 수 있습니다.
값에 직접 접근하기 위해선 *변수명 처럼 역참조 연산자를 사용해야 합니다.
int func(int* a)
{
*a = 30;
return 0;
}
int main()
{
int a = 0;
func(&a); // 변수 앞에 &을 붙이면 주소를 뽑아낼 수 있음
cout << a; // 출력 결과 : 30
}
Call by Reference
int func(int& a) {}
Call by Reference는 Call by Address와 Call by Value를 섞어놓은 것이라고 보면 편합니다.
넘겨주는건 주소가 아닌 일반 변수이지만 매개변수는 자료형& 변수명 꼴로 적혀있고
포인터가 아닌데도 주소에 접근할 수 있습니다.
이는 참조자 변수라고 불리며 매개변수와 같은 메모리 공간을 공유하기 때문에 값을 공유하고 주소또한 마찬가지 입니다.
int func(int& a)
{
a = 30;
return 0;
}
int main()
{
int a = 0;
func(a);
cout << a; // 출력 결과 : 30
}
배열
C++에서의 배열은 포인터와 매우 유사한 성질을 띄고 있습니다.
사실 배열 자체는 포인터와 같다고 봐도 무방합니다.
간단하게 int 배열을 선언해볼까요?
int arr[3] {1, 5, 4};
이렇게 배열을 선언하면 메모리 상에선 이렇게 저장됩니다.
자료형이 int이기 때문에 주소를 4칸씩 잡아먹고 있고 옆으로 딱딱 붙어있죠.
사실 arr는 arr[0]의 주소를 갖고 있습니다.
*arr 와 arr[0]은 출력하는 값이 똑같죠.
그래서 배열에 정수를 더하면 그 더한 값만큼의 인덱스의 주소에 접근할 수 있는겁니다.
arr[5] 와 *(arr + 5)는 똑같습니다.
따라서 포인터 변수에 배열을 넣을수도 있고 배열 변수에 포인터를 넣을 수도 있습니다.
int func(int* arr)
{
*arr = 10;
return 0;
}
int main()
{
int arr[3] = {1, 5, 4};
func(arr);
cout << arr[0]; // 출력결과 10
}
마치는 말
요번 수업을 마치고 C++과 한층 친해졌습니다.
물론 언어가 기괴한건 아직까지 마찬가지지만요..
그래도 C#에선 쓸일이 없는 주소에 대해서 티끌만큼 이해했습니다.
'개발 > C++' 카테고리의 다른 글
게임 프로그래밍 | C++ | 콘솔 / 입력 (1) | 2024.04.08 |
---|---|
자료구조 | C++ | 클래스 (1) | 2024.04.05 |
게임 프로그래밍 | C++ | 랜덤 (2) | 2024.03.22 |
자료구조 | C++ | 문자열 (3) | 2024.03.17 |
자료구조 | C++ | 기본 개념 (1) | 2024.03.07 |