C++ 스터디 #6: 함수

시간 2021년 5월 20일 목요일 20:30 ~ 22:10
장소 Google Meet
참가자 강동륜 김도형 김석호 김진우 김희연 김도균 문준영 박경민 시주성 심훈용 안영현 엄경현 오성근 우재원 윤호연 이미경 이언상 임승환 정인하 정지웅 조동민 김지훈 최원섭 함영석 홍제준 황선용
  • 지역변수: 특정 블록 내에 선언된 변수는 그 블록 내에서만 사용할 수 있다.

{
    int a = 1;
}
cout << a;    // Error code E0020: 식별자 "a"이(가) 정의되어 있지 않습니다.

  • 전역변수: 가장 밖에 선언된 변수, 모든 곳에서 접근이 가능하다.

#include <iostream>
using namespace std;

int a = 1;

int main() {
    cout  << a << endl;    // 1
    {
        cout << a;    // 1
    }
    return 0;
}

블록은 중괄호로 묶인 부분이다. 블록 내에 선언된 변수는 블록에 진입할 때 생성되고 블록을 빠져나올 때 소멸한다.

cout << a;    // Error code E0020: 식별자 "a"이(가) 정의되어 있지 않습니다.
{
    int a = 1;
    cout << a;    // 1
}
cout << a;    // Error code E0020: 식별자 "a"이(가) 정의되어 있지 않습니다.

  • 쉐도잉 (shadowing)

블록 내부의 변수는 외부의 변수와 같은 이름을 가질 수 있다. 이 경우 내부와 같은 이름의 외부의 변수는 잠시 가려진다.

int a = 1;
{
    int a = 2;
    cout << a;    // 2
}

→ 이런 식으로 사용할 수는 있지만 이런 코드는 유지·보수 뿐만 아니라 프로그램의 작동을 이해하기 어렵기 때문에 지양해야 한다.


함수는 특정 작업을 수행하기 위해 짜인 코드 덩어리이다. 함수를 사용하면 같은 긴 코드를 반복해서 사용하는 것을 막을 수 있다. 프로그램에서 특정 작업을 반복해야 할 때는 해당 작업을 수행하는 함수를 작성해서 프로그램이 필요할 때마다 호출할 수 있다.

함수는 메인함수 앞에 다음과 같이 선언한다.

<반환 자료형> <함수 이름> (<매개변수>) {
    <함수 몸체>
}

int add(int a, int b) {
    return a + b;
}

  • 반환 자료형 : 함수의 실행을 마치고 반환하는 데이터의 자료형을 나타낸다. 반환하는 값이 없다면 void형을 사용한다.
  • 함수 이름 : 함수의 이름은 해당 함수의 기능을 쉽게 알 수 있도록 짓는 편이 좋다.
  • 매개변수 : 함수를 호출할 때 전달되는 인수의 값을 저장하는 변수를 나타낸다.
  • 함수 몸체 : 함수의 기능이 작성된다.

매개변수는 함수에서 전달받은 인수를 함수 내부로 전달하기 위해 사용하는 변수이다. 함수 매개 변수는 함수 블록 내에서만 사용되는 지역변수로, 함수 밖에서는 사용할 수 없다.

인수는 함수를 호출할 때 함수에 전달한 값이다.

add(1, 2);    // add라는 함수에 1과 2라는 인수를 전달한다.

사용자 정의 함수를 메인함수 앞에 선언하는데 이때 함수가 길어지거나 많아지게 되면 메인함수를 찾기 힘들고 코드를 보기 힘들어지므로 함수를 메인함수 다음에 선언할 수 있다. 하지만 컴파일러가 위에서부터 차근차근 읽기 때문에 사용자 정의 함수를 메인 함수 밑에 선언하게 되면 메인 함수 내에서 호출하는 함수를 인식할 수 없다. 이를 해결하기 위해 메인 함수 전에 함수 원형을 선언하여 컴파일러에게 해당 함수를 사용하겠다고 알려줘야 한다.

함수 원형은 다음과 같이 선언한다.

<반환 자료형> <함수 이름> (<매개변수>);

int add(int a, int b);

함수 원형을 메인 함수 이전에 선언하면 메인 함수 다음에 반드시 해당 함수를 정의해야 한다.

  1. 사용자 정의 함수 원형 선언
  2. 메인 함수
  3. 사용자 정의 함수 정의

함수 정의는 메인 함수 위에 사용자 정의 함수를 선언할 때와 똑같이 해주면 된다.

2.4.1. 값으로 전달(pass by value)

인수를 값으로 전달하는 방식은 지금까지 함수를 호출할 때 사용하던 방식이다.

add(1, 2);    // add함수에 인수 1과 2를 값으로 전달한다.

인수를 값으로 전달할 때에는 함수 매개변수에 인수 값을 복사한다. 따라서 함수 내에서 매개변수로 받은 값을 변경한다고 해서 메인 함수의 변수 값이 실제로 변경되지 않는다.

#include <iostream>
using namespace std;

void change(int num);

int main() {
    int a = 1;
    cout << "a = " << a << endl;
    change(a);
    cout << "a = " << a;
}

void change(int num) {
    cout << "num = " << num << endl;
    num = 2;
    cout << "num = " << num << endl;
}

// 출력
// a = 1
// num = 1
// num = 2
// a = 1

2.4.2. 참조로 전달(pass by reference)

인수를 참조로 전달할 때에는 매개 변수를 인수에 대한 참조로 선언하여 원본 데이터로 직접 전달하므로 함수에서 데이터를 직접 수정할 수 있다.

참조로 전달할 때는 함수를 선언할 때 매개변수를 참조자로 선언하므로 int&와 같이 &를 써서 int형 변수에 대한 참조임을 나타낸다. 함수를 호출할 때에는 값으로 전달할 때와 마찬가지로 &를 사용하지 않는다.

#include <iostream>
using namespace std;

void change(int& num);

int main() {
    int a = 1;
    cout << "a = " << a << endl;
    change(a);
    cout << "a = " << a;
}

void change(int& num) {
    cout << "num = " << num << endl;
    num = 2;
    cout << "num = " << num << endl;
}

// 출력
// a = 1
// num = 1
// num = 2
// a = 2


  • 재귀호출 : 함수에서 자기 자신을 호출하는 방식

#include<iostream>
using namespace std;

void hello()
{
    cout << "Hello \n";

    hello();
}

int main()
{
    hello();

    return 0;
}

재귀호출을 사용할 경우, 예시와 같이 자기 자신을 계속 호출하게 되는 스택 오버플로우를 주의해야 한다. 이를 막기 위해 재귀호출을 사용할때 다음과 같이 종료 조건을 만들어주어야 한다.

#include<iostream>
using namespace std;

void hello(int count)
{
    if (count == 0)
    {
        return; // count가 0이 되면 종료
    }
        
    cout << "Hello \n";

    hello(--count); // --count는 count-1과 같음
}

int main()
{
    hello(10);

    return 0;
}


  • 함수 오버로딩 : 같은 함수의 이름을 가지고 있지만, 매개변수나 리턴 타입 등의 특징이 다른 여러 함수를 만드는 것

함수 오버로딩은 컴파일 에러를 일으킬 것 같아 보이지만, 컴파일러는 함수 호출에 사용된 인수를 기반으로 호출할 함수를 결정하기 때문에 에러를 일으키진 않는다.

매개변수들의 합을 출력해주는 add() 라는 함수를 생각해보자.

int add(int x, int y)
{
    return x + y;
}

double add(double x, double y)
{
    return x + y;
}
위의 두 함수는 매개변수가 각각 int 와 double 형으로 다르므로, 함께 선언될 수 있다.

int add(int x, int y, int z)
{
    return x + y + z;
}
이 함수는 매개변수의 개수가 3개로 다르므로, 함께 선언될 수 있다.

그러나 함수 오버로딩에서는 함수의 반환 타입은 고려하지 않는다.

예를 들어 랜덤 숫자를 반환하는 함수를 선언하려 한다 생각해보자.

int getrandomvalue();
double getrandomvalue();
이 두 함수는 함께 선언될 수 없다. () 속의 매개변수가 같기 때문이다. (둘 다 없으므로 : 같음)

이처럼 매개변수의 차이가 없을때는 함수 오버로딩을 사용할 수 없고, 서로 다른 이름의 두 함수를 선언해야 한다.


주어진 두 자연수의 최대공약수와 최소공배수를 구하는 함수를 만들어 출력하는 프로그램 작성하기

12 30
최대공약수 :  6
최소공배수 :  60


두 변수의 값을 교환하는 함수 작성하기

5 10
a = 5, b = 10
swap
a = 10, b = 5


터틀봇은 전진과 제자리 회전만 가능한 로봇이다. 주어진 코드의 빈칸을 채워서 터틀봇이 변 개수와 변 길이를 입력받아 전진하고 회전하며 다각형을 그리는 프로그램을 완성하라.

#include <iostream>
using namespace std;

void moveForward(________) {
	cout << ________________ << endl;
}

void rotate(________) {
	cout << ________________ << endl;
}

void drawPolygon(________________) {
	________________________________
	________________________________
	________________________________
	________________________________
}

int main() {
	________________________________
	
	________________________________
	________________________________
	________________________________
	________________________________
	________________________________

	drawPolygon(________________);

	return 0;
}


변 개수 : 6
변 길이 : 2

2만큼 전진
60도 회전
2만큼 전진
60도 회전
2만큼 전진
60도 회전
2만큼 전진
60도 회전
2만큼 전진
60도 회전
2만큼 전진
60도 회전


최대공약수를 구하는 재귀함수 작성하기

48 32
최대공약수 :  16


  • activity/public/2021/cpp/210520.txt
  • 마지막으로 수정됨: 3년 전
  • 저자 david