혼자 연구하는 C/C++ 6장 정리(함수)

2015. 3. 1. 04:00창고

728x90
728x90

1. 함수의 구성원리

함수의 정의

함수는 프로그램을 구성하는 단위로 프로그램의 부품 역할을 한다.

프로그램 : 함수 = 컴퓨터 : 부속품


함수

type name(인수 목록)

{

함수의 본채

}


name - 함수의 이름이며 이 이름을 통해 함수를 호출한다. 의미를 잘 표현할 수 있는 이름으로 하는것이 좋다.

인수목록 - 함수가 해야 할 일의 세부 사항을 지정하며 함수의 작업거리라고 할 수 있다.

type - 함수가 리턴하는 값의 데이터형이며 함수의 작업결과라고 할 수 있다.

본체 - { } 괄호 안에 실제 함수의 코드가 위치한다.


ex)

1
2
3
4
5
6
7
8
9
10
11
int Max(int a, int b)
{
    if(a>b)
    {
        return a;
    }
    else
    {
        return b;
    }
}
cs


Max라는 이름의 함수이고 int a, int b의 두 개의 정수값을 전달한다.

함수의 본체는 if문을 통해 두 값 중 큰 값을 리턴한다.


인수란?

인수(Parameter)는 호출원에서 함수에게 넘겨주는 작업 대상이라고 할 수 있다.


ex)

(x,y) 위치에 점수 Score를 출력하는 함수 : PrintScore(int x, int y, int Score)

실수 x의 제곱근을 구하는 함수 : GetSqrt(double x)

문자열에서 공백의 개수를 조사하는 함수 : GetSpaceNum(char* str)


목적에 따라 인수가 없을 수 있다. clrscr() 같은 함수.


인수는 형식인수와 실인수로 구분된다.

형식인수 - 함수의 인수 목록에 나타나는 인수를 말한다.

실인수 - 호출부에서 함수와 함께 전달되는 인수를 말한다.


return

인수가 호출원으로부터 전달되는 작업 대상이라면 리턴값은 함수가 호출원으로 돌려주는 작업 결과이다.

ex)

1
2
3
4
5
6
7
double div(double a, double b)
{
    if(b!=0)
    {
        return a/b;
    }
}
cs


double형 두 개를 받아 나누기 연산을 하고 double형으로 리턴하는 함수이다.


void형 함수

함수는 작업한 결과를 리턴값으로 돌려줄 수 있는데 모든 함수가 리턴값을 가질 필요는 없다.

리턴할 값이 없는 함수도 있는데 이런 함수를 void형 함수라 한다.


함수의 다른 이름

함수는 동작이라기보단 기능이라고 번역하는게 적당하다고 할 수 있다.


C의 함수에 대한 두 가지 개념.

함수 - 특정 계산을 수행하며 리턴값이 있다.

프로시저 - 특정 작업을 수행하며 리턴값이 없다.


C의 함수의 특징

- 함수끼리는 서로 평등한 관계에 있으며 상호 수평적이다.

- 함수 중에서 가장 기본이 되는 함수를 main 함수라 부르고 프로그램의 시작점이 된다.

- 리턴값은 있을 수도 있고 없을 수도 있다.

- 항상 단독으로 문장 구성이 가능하다. (리턴이 있는 함수는 리턴값을 버리게 된다.)

- 값에 의한 호출 방식을 사용한다.


2. 헤더파일

C 컴파일러의 컴파일 방식?

프로그래밍 언어는 해석 방식에 따라 인터프리터 방식과 컴파일 방식으로 나누어진다.

컴파일 방식이 훨씬 성능이 좋기 때문에 대부분의 언어가 컴파일 방식을 택한다. 물론 C도!

컴파일 방식은 소스를 읽어 기계어로 한꺼번에 번역하는 방식이다.

번역을 몇 번 하느냐에 따라 1패스, 2패스 등으로 구분된다.

C는 1패스 방식이기 때문에 한번에 소스를 읽을 방법이 필요했기 때문에 함수의 원형이 필요하다.


함수의 원형

원형(ProtoType)이란 함수에 대한 정확한 정보라는 뜻이며 리턴 타입과 함수 이름, 인수 리스트 등의 정보로 구성된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
int Max(int a, int b); //function prototype
 
void main()
{
    ...
 
    int a, b;
 
    ...
 
    Max(a,b);
    ...
}
int Max(int a, int b){...} 
cs


C가 1패스 방식을 사용하고 딱 한 번 읽어서 번역하기 때문에 main함수보다 늦게 나오면 안된다.

그래서 int Max 함수의 원형을 선언하여 Max가 함수라는 것과 어떤 타입의 인수를 요구하는지 미리 알려주는 것이다.


원형의 형식은 완전한 원형과 간략한 원형이 있다.

1
2
int Max(int a, int b);
int Max(intint);
cs

1행이 완전한 원형, 2행이 간략한 원형.


헤더 파일

표준 함수의 원형을 미리 작성해 놓은 파일이다. #include를 통해 사용할 수 있다.



모듈

함수를 사용하기 전에 원형만 선언한다면 함수의 본체를 어디에 두어도 상관 없다.

심지어 다른 소스 파일에 둘 수 있다. 프로젝트는 하나의 실행 파일을 만들기 위한

모듈의 집합이며 한 프로젝트 안에 여러개의 모듈(cpp 파일)을 둘 수 있다.

그림과 같이 하나의 소스 파일에 모든 함수를 작성하는게 아니라 함수의 그룹별로 모듈을

구성하고 각 모듈은 헤더 파일을 통해 제공하는 함수 목록을 밝히면 된다.


모듈 분할 컴파일 방식의 이점

- 컴파일 속도가 빠르다. 수정한 모듈만 다시 컴파일 하기때문에 잘게 나눌 수록 빨라진다.

- 분담 작업이 가능하다.

- 프로젝트 관리가 쉽다. 관련 함수를 보고 문제가 생기는 부분을 찾기 쉽다.

- 모듈을 재활용할 수 있다. 기능적으로 독립적인 함수의 집합이기 때문에 재사용이 가능하다.


3. 함수 호출 방식

인수란 호출원에서 함수에게 일을 시키기 위한 정보인데 인수를 어떻게 전달하는가에 딸

값 호출(call by value) 방식과 참조 호출(call by reference) 방식이 있다.

실인수의 값이 변경되는가 아닌가의 차이.


값 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
void main()
{
    ...
    j = plusone(i);
    printf j
    ...
}
 
int plusone(int a)
{
a = a+1;
    return a;
}
cs


1을 더하는 함수인 plusone은 a에 1의 값을 더한 뒤에 리턴 하는데

실인수 i로 넘겼지만 실제론 형식인수 a에 대입되어 형식 인수가 변환된다.

그래서 실제로는 실인수 i는 바뀌지 않고 실인수에 전혀 영향을 미치지 않는다.


함수 호출 시 전달되는 대상이 실인수 그 자체가 아니라 실인수의 이기 때문에 이런 호출 방식을 값 호출이라 부른다.




참조 호출

위의 값 호출 예제와 똑같은 동작을 하되 참조 호출 방식으로 변수 값을 1 증가시키는 함수를 작성한다.


1
2
3
4
5
6
7
8
9
10
11
12
void main()
{
    ...
    plusref(&i);
    printf i
    ...
}
 
void plusone(int* a)
{
    *+= 1;
}
cs


결과는 같지만 동작하는 방식이 다르다. 연산식에서 *a로 a가 가리키고 있는 번지에 들어 있는 값을 가리킨다.

a가 &i로 인자를 받았으므로 실인수 i를 직접 접근할 수 있는 것이다.

plusone 함수 - 값을 넘기면 1증가된 값을 리턴한다.

plusref 함수 - 값이 들어있는 번지를 주면 이 번지에 들어 있는 값을 1 증가시킨다.


C++의 참조 호출

C++에선 포인터를 이용한 방법 외에 레퍼런스라는 개념으로 참조 호출을 추가 지원한다.


4. C언어의 참조 호출에 대한 생각 - 매우 중요! * 2만개!

C언어의 포인터를 통한 참조 호출은 사실 엄밀한 의미의 참조 호출이라고 볼 수 없다.

이유 - 변수의 번지값(Address Value)이라는 특수한 '값'이기 때문.

이 번지값으로 실인수를 조작할 수 있도록 흉내만 낼 수 있으며 참조 효과가 발생할 뿐이다.

C언어의 함수 호출방식은 결국 모두 값 호출 방식이되 편의상 포인터를 통한 호출을 참조 호출이라 부를 뿐.

C++의 레퍼런스는 C의 포인터를 사용하는 방법보다 더 발전된 형태긴 하지만 이것도 결국은 값 호출!

레퍼런스 또한 내부적으론 포인터를 흉내낸 방식이다.

결국 레퍼런스는 포인터를 흉내내서 참조 호출을 흉내내는 기만적인 참조 호출!


5. 전처리기

전처리기(PreProcessor)는 말 그대로 앞서 먼저 처리하는 명령이란 뜻이다.

컴파일하기 전에 소스를 재작성하는 역할을 한다.


다소 독특한 제약 - 반드시 행을 모두 차지해야 하며 C 코드를 같이 쓸 수 없다. 프리 포맷의 예외인 셈.


#include

괄호안의 파일을 읽어와 현재 위치에 삽입하는 역할을 한다.

stdio.h와 같은 헤더 파일을 미리 읽어와 컴파일 하여 stdio.h 내부의 함수의 원형, 데이터 타입 등을 공짜로 사용할 수 있다.


표준 헤더와 사용자 정의 헤더 가져오기

#include <file.h> - C에서 제공하는 표준 헤더파일을 포함시키는 방법.

#include "file.h" - 사용자가 직접 작성한 헤더 파일을 포함시키는 방법.


#define

매크로 상수를 정의하는 데 사용한다.


#define 매크로명 실제값


기억하기 쉬운 이름으로 주고 실제값을 뒤에 써 준다.


#define GAMETIME 240


매크로 상수는 기억의 용이함을 위해 사용되기도 하지만 특정값의 일괄적인 수정을 위해서도 용이하게 사용된다.


1
2
3
4
5
6
7
8
9
10
void main()
{
    ........ 240 ....
    ......240 ..........
    ........    
     .............    
    ...............240...
    240 .....................
}
 

cs


1
2
3
4
5
6
7
8
9
10
void main()
{
    ........ GAMETIME ....
    ......GAMETIME ..........
    ........    
     .............    
    ...............GAMETIME ...
    GAMETIME .....................
}
 
cs


#define문의 일반적인 주의 사항

- 전처리문이지 코드를 생성하는 명령이 아니다. 세미콜론 절대 금지!

- 매크로의 이름도 일종의 명칭이기 때문에 명칭 규칙에 맞게 작성한다.

- 매크로 이름에는 공백은 들어갈 수 없다. 실제 값은 공백을 가질 수 있다. #define ERR "똑바로 하셈"

- 문자열 상수 내에 있는 매크로나 다른 명칭의 일부로 포함된 경우는 치환되지 않는다.

- 매크로는 중첩 가능하다. #define A 3 #define B (A*2). 순서에 맞게 참조되어 계산한다.

- 값을 가지지 않는 빈 매크로도 정의할 수 있다. 조건부 컴파일 지시자와 함께 사용된다. 아무 값도 가지지 않음을 명시할 때도 사용한다.


매크로 함수

#define 전처리기를 사용하여 함수를 흉내낸 것이다.

#define dubae(i) i+i;

dubae(3)은 3+3으로 치환되어 전체식은 6으로 평가된다.


주의할점 

- 매크로 함수의 전체식을 괄호로 싸야 한다.

전처리기의 동작은 기계적인 치환에 불과하다!

ex) -dubae(3)을 하면 -6이 될 것 같지만 실제론 -3+3이 되어 0이라는 결과가 나온다!

dubae(i) (i+i)라고 정의하면 올바른 결과인 -6이 나온다.


- 매크로의 인수들도 개별적으로 괄호를 싸 준다. 위와 같은 이유!

ex) jegop(i) (i*i)일 때

jegop(3+1)은 (3+1*3+1)로 치환. 이상한 결과가 나온다.


- 매크로 함수는 인수의 타입 같은 것을 점검하지 않는다.

- 매크로 함수에 여러 개의 명령을 동시에 포함시킬 수 있다.

ex) #define printmsg(x,y,str) { gotoxy(x,y); puts(str); }


- 매크로 함수 호출문에서 ++, -- 등의 증감연산자나 +=, *=등의 복합 대인 연산자는 삼가라.

- 쓸데 없는 매크로는 만들지 마라.

ex) #define infinite for(;;;)

728x90
반응형