Coding/Unreal, C++

[C++ Primer Plus] 7. Funtions: C++'s Programming Modules

Function Review

  • 함수를 사용하려면 다음 절차를 거친다.
    1. 함수를 정의한다.
    2. 함수 프로토타입을 소스코드 서두에 쓴다.
    3. 함수를 호출한다.

Why Prototypes?

  • 함수 프로토타입은 컴파일러에게 함수 인터페이스를 알려주는 역할이다. 어떤 값이 리턴 되는지, 어떤 타입의 매개변수들을 받아야 하는지 등을 알려준다.
  • 그러면 왜 프로토타입을 쓰는지 궁금할 수 있다.
    • 그냥 main()에서 함수를 쓸 때마다 거기가서 찾아보면 되지 않나?
      • 컴파일러는 그렇게 돌아가지 않는다. 너무 비효율적이기 때문이다. 애초에 함수의 정의가 다른 파일에 있을 수도 있다.
    • 그럼 main() 위에 바로 함수를 정의하면 컴파일 되던데?
      • 그건 맞다. 하지만 이는 별로 선호되지 않는다. main()이라는 주요 과정을 바로 볼 수 없기 때문이다.

Functions and Arrays

  • 함수에 Array를 매개변수로 넘겨줄 때, 애매한 문법이 있다.
int sum_arr1(int arr[], int n);
int sum_arr2(int * arr, int n);
  • 위 두 함수의 첫번째 매개변수인 arr은 뭐가 다를까? 정답은 다른게 없다는 것이다.
  • 적어도 매개변수에 쓰일 때, 두 서술 방식은 완벽히 동일하다. 결국은 array의 메모리 시작 주소를 넘겨준다.
  • 하지만 프로토타입을 읽는 입장에서 두 의미는 좀 다르게 읽힐 수 있다. 예를 들어, sum_arr1의 arr은 int array를 가리키는 포인터다. 하지만 sum_arr2의 arr은 단일 int를 가리키는 포인터인지, 배열 포인터인지 바로 알기 힘들다.
  • 책에서는 이를 구분하기 위해 배열을 가리키는 포인터는 int arr[]의 방식으로 쓰겠다고 말한다.

Functions Using Array Ranges

  • 전통적인 C/C++이 매개변수로서 배열을 위 예시처럼 배열 포인터와 그 크기를 받아내서 처리한다. 하지만 다른 방식도 가능하다. 배열의 range를 넘기는 것이다.
int sum_arr(const int * begin, const int * end);

Pointers and const

  • const 키워드와 pointer를 함께 쓸 때, 애매한 부분이 존재한다.
int num = 5;
const int * ptr = #
  • ptr은 num의 주소값을 담고 있다. 근데 이게 const, 즉 상수라는 뜻은 ptr이 담고 있는 주소값 자체를 바꾸지 못한다는 뜻일까? 아니면 ptr을 통해서 num 값을 바꾸지 못한다는 뜻일까? 아니면 둘 다 일까?
  • 정답은 위 경우엔 두번째다.
  • 쉽게 외우려면 const는 바로 옆에 있는 값의 상수성을 보장한다고 이해하면 편하다. 위의 경우에서 const는 int에 영향을 미친다. 만약 ptr이 다른 주소값을 담지 못하게 하려면 다음과 같이 정의해야 한다.
int const * ptr = #
ptr = &other; // ERROR
  • 만약 ptr이 다른 주소값으로 바뀔 수도 없고 ptr이 가리키는 값도 바꿀 수 없게 하려면 다음과 같이 여러번 써야 한다.
const int const * ptr = #
*ptr = 10; // ERROR
ptr = &other; // ERROR

Functions and Structures

  • C++에서 Structure는 새로운 타입처럼 쓰인다고 설명했었다. 이는 함수의 매개변수에서도 동일하다. 즉, 기본적으로 pass by value, return value다.
  • 당연히 structrue의 크기 문제로 value로 넘겨주면 성능 손해가 있다. 이를 방지하기 위해 Structure Address를 넘겨줄 수 있다.
void show_polar (const polar * pda);

Functions and string Class Objects

  • C++이 제공하는 std::string 클래스 또한 Structure와 같은 취급이다.

Pointers to Functions

  • 함수 포인터를 쓰려면 다음과 같은 과정을 거쳐야 한다.
    1. 함수의 주소를 얻는다.
    2. 함수의 주소를 포인터 변수에 저장한다.
    3. 변수를 사용해 저장된 함수를 호출한다.
  • 함수의 주소를 얻는 방법은 아주 간단하다! 함수의 이름만 쓰면 된다.
process(think); // think 함수의 주소를 매개변수로 넘겨준다.
process(think()); // think 함수의 결과값을 매개변수로 넘겨준다.
  • 함수 포인터형 변수를 만드는 건 좀 복잡하다. 함수의 선언과 비슷하게 쓰되, *를 tag 왼쪽에 쓰고 괄호로 감싼다.
double (*pf)(int); // double을 리턴하고 int 하나를 매개변수로 받는 함수의 주소를 담는 변수 pf
  • 이렇게 만든 함수 포인터를 쓰는 방법은 두가지다.
(*pf)(5);
pf(5); // C++에서만 가능한 문법. 하지만 pf가 함수포인터인지 바로 알 수 없기 때문에 권장하지 않는다.

References

  • C++ Primer Plus 6th, Chapter 7