책정리/혼자 연구하는 C,C++ 1

6장 함수

GONII 2015. 2. 19. 16:46

6.1 함수의 구성원리

6.1.1 함수의 정의

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

프로그램이 해야 할 일을 각 부품들(=함수)이 나누어 맡으며 그 중에서도 가장 핵심이 되는 함수(=main)의 통제 아래 모든 함수들이 체계적으로 실행되어 전체적으로 프로그램이라는 하나의 완성을 이루는 것이다.

  • 함수의구분

    표준 함수

    사용자 정의 함수

    6.1.2 함수

  • 사용자 정의 함수 기본 형식

    type name(인수 목록)

    {

    함수의 본체

    }

  • name

    함수의 이름

    이 이름을 통해 함수를 호출

    함수의 의미를 잘 표현할 수 있는 이름으로 정해줘야 함

  • 인수 목록

    함수가 해야 할 일의 세부 사항을 지정

    함수에게 일을 시키기 위해서는 작업에 필요한 값을 전달

    없을 수도 있고, 여러개 있을 수도 있음.

    인수 목록에 필요한 인수의 타입과 이름을 밝힌다.

  • type

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

    돌려주는 값의 타입이 바로 함수의 타입이다.

    리턴하는 값이 없으면 void함수

  • 본체

    {} 괄호 안에 실제 함수의 코드가 위치함

    함수의 고유 기능을 수행하는 코드를 작성

  • 예제 MaxFunc

#include <stdio.h>

int Max(int a, int b)

{

if( a > b ) {

return a;

} else {

return b;

}

}

void main()

{

int a,b,m;

printf("두 수를 입력하세요 : ");

scanf("%d%d", &a,&b);

m=Max(a,b);

printf("두 수 중 큰 값은 %d입니다"\n,m);

}

프로그램은 항상 main 함수에서 시작한다.

함수별로 특정 기능을 담당하도록 프로그램의 기능을 분할해 놓으면 코드의 구조가 만들어지기 때문에 관리하고 재사용하기 편리

비슷한 작업을 반복적으로 계속 수행할 때는 전담 함수를 만들어 놓고 필요할 때마다 이 함수만 호출하면 되므로 코드의 반복을 방지 할 수 있다.

6.1.3 인수

  • 인수(Parameter)

    호출원에서 함수에게 넘겨주는 작업 대상

    두 함수 사이의 정보 교환에 사용되므로 매개 변수(Argument)라고도 함

  • 인수의 구분

    형식인수 : 함수의 인수 목록에 나타나면

    실인수 : 함수 호출부에서 함수와 함께전달되는 인수

  • 예제 AddFunc

#include <stdio.h>

int Add(int a, int b)

{

return a+b;

}

void main()

{

int a,b;

a=2;

b=3;

printf("2+3=%d\n",Add(a,b));

printf("5+6=%d\n",Add(5,6));

}

6.1.4 return

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

결과를 리턴할 때는 return문을 사용

  • return문의 기능

    함수의 결과값을 호출원으로 돌려주는 기능

    함수를 강제 종료시키는 기능

  • 예제 Dec2Hex2

#include <stdio.h>

char Dec2HexChar(int d)

{

if(d>=16) {

return '?';

}

return d+'0'+(d>9)*7;

}

void main()

{

int input;

for (;;) {

printf("0~255사이의 수를 입력하시오.(끝낼 때 -1_ : ");

scanf("%d", &input);

if (input==-1) {

break;

}

printf("입력한 수의 16진수 표기 = %c%c\n", Dec2HexChar(input>>4),Dec2HexChar(input&0xf));

}

}

6.1.5 void형 함수

리턴할 값이 없는 함수

  • 예제 voidFunc

#include <stdio.h>

void PrintWait()

{

printf("지금 열심히 작업중입니다. 잠시만 기다려주세요\n");

}

void main()

{

PrintWait();

printf("1+2=%d\n",1+2);

}

6.1.6 함수의 다른 이름

  • 함수 (function)

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

    반드시 수식 내에서만 사용할 수 있으며 함수 단독으로 문장을 구성할 수 없다.

    수학적 의미의 함수와 거의 유사

  • 프로시저 (procedure)

    특정 작업을 수행하며 리턴값이 없다

    리턴값이 없기 때문에 수식 내에서는 사용할 수 없으며 단독으로 문장을 구성할 수 있다.

    C의 void함수가 이에 해당

6.2 헤더파일

6.2.1 함수의 원형

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

컴파일 방식은 소스를 읽어 기계어로 한꺼번에 번역하는 방식인데 번역을 몇 번에 나누어 하느냐에 따라 1패스, 2패스 등으로 구분됨.

C의 1패스 방식 때문에 함수의 원형이라는 것이 필요하다.

  • 예제 MaxFunc2

#include <stdio.h>

void main()

{

int a,b,m;

printf("두 수를 입력하세요 : ");

scanf("%d%d",&a,&b);

m=Max(a,b);

printf("두 수 중 큰 값은 %d입니다.\n"m);

}

int Max(int a, int b)

{

if(a>b) {

return a;

} else {

return b;

}

}

예제 MaxFunc2 입력시 에러 메세지 출력 - 최초 main에서 Max라는 명칭을 만났을 때 Max의 실체를 파악하지 못함

대입이 선언보다 앞에 나와 있기 때문에 에러

main이 A를 호출하고, A가 C,D를 호출하고 C가 B를 호출한다면

B-C-D-A-main 순으로 배치

함수가 많아지면 복잡해지므로 이러한 방법은 해결책이 되지 못한다.

그러므로 원형(ProtoType)을 선언하여 컴파일러가 알 수 있도록 해야 함

  • 예제 MaxFunc3

#include <stdio.h>

int Max(int a, int b); // 원형 선언

void main()

{

int a,b,m;

printf("두 수를 입력하세요 : ");

scanf("%d%d",&a,&b);

m=Max(a,b); // 이미 원형이 선언되었으므로 Max함수를 사용할 수 있다.

printf("두 수 중 큰 값은 %d입니다.\n"m);

}

int Max(int a, int b) // 본체는 뒤쪽에 있다.

{

if(a>b) {

return a;

} else {

return b;

}

}

6.2.2 원형의 형식

함수의 원형은 컴파일러에게 함수에 대한 정보를 제공하기 위해 작성

함수의 본체는 적지 않고 리턴 타입, 함수 이름, 인수 목록만 적는다.

함수를 정의하는 형식에서 본체를 빼고 뒤에 세미콜론을 붙이면 됨.

   

int Max(int a, int b); // 완전한 원형 - 형식 인수명도 적어준다.

int Max(int, int) // 간략한 원형 - 인수의 타입만 밝힌다.

사용자에게 많은 정보를 제공하기 위해 모두 완전한 형태로 작성하는게 좋다.

6.2.3 헤더 파일

#include <stdio.h>가 바로 표준 함수의 원형 선언문이다.

표준함수들의 원형은 컴파일러를 만들 때 이미 결정되어 있기 때문에 컴파일러 제작사가 원형을 미리 작성하여 컴파일러와 함께 배포한다.

헤더파일

함수

stdio.h

conio.h

stdlib.h

math.h

string.h

표준 입출력에 관한 함수

키보드 및 화면 입출력 함수

자료 변환 함수들

수학 함수들

문자열 조작 함수들

printf, scanf, puts

getch, cprintf, kbhit

atoi, itoa, malloc, free

sin, cos, log

strcpy, strlen

6.2.4 모듈

  • 모듈 분할 컴파일(또는 다중 모듈 컴파일)

    하나의 실행 파일을 만들기 위한 소스를 여러 개로 나누어 개발하는 방식

  • 모듈 분할 컴파일의 장점

    1. 컴파일 속도가 빠르다. 모듈을 잘게 나눌수록 컴파일 속도는 더 빨라질 것이다.

    2. 분담 작업이 가능하다.

    3. 프로젝트 관리가 쉽다. 하나의 긴 소스에서 함수들이 마구 뒤섞여 있다면 당장 작업할 부분을 찾기도 어렵고, 관리도 힘들다.

    4. 모듈을 재사용 할 수 있다.

    MultiModul.cpp에는 Max함수가 정의되어 있지 않지만 main함수에서 Max함수를 호출한다.

    util.h에 원형이 선언되어 있고 uitl.cpp에 Max함수의 본체가 작성되어 있기 때문

    main함수를 컴파일 할 때 Max함수의 원형만 알고 있으면 이 함수 호출부를 컴파일 할 수 있으며, 각 모듈이 컴파일된 후 링커에 의해 연결된다.

    6.2.5 함수 제작

  • 예제 BoxMessage

#include <stdio.h>

#include <string.h>

void BoxMessage(char *str);

void main()

{

BoxMessage("박수를 그리고 그 안에 문자열을 출력한다.");

BoxMessage("전달된 문자의 길이에 적당한 박스를 스스로 계산한다.");

BoxMessage("신기하군");

}

void BoxMessage(char *str)

{

int i;

int len;

len=strlen(str);

puts("");

for (i=0; i<len+4; i++) {

putch('-');

}

puts("");

printf("| %s |\n",str);

for (i=0; i<len+4; i++) {

putch('-');

}

puts("");

}

  • 예제 BoxMessage2

#include <stdio.h>

#include <string.h>

void BoxMessage(char *str);

void putchs(int ch, int n);

void main()

{

BoxMessage("박수를 그리고 그 안에 문자열을 출력한다.");

BoxMessage("전달된 문자의 길이에 적당한 박스를 스스로 계산한다.");

BoxMessage("신기하군");

}

void BoxMessage(char *str)

{

int len;

len=strlen(str);

puts("");

putchs('-', len+4);

printf("| %s |\n",str);

putchs('-', len+4);

}

void putchs(int ch, int n)

{

int i;

for (i=0; i<n; i++) {

putch(ch);

}

puts("");

}

  • 과제 Power

    두 개의 정수 a와 b를 입력받아 a의 b승을 구하는 power 함수를 작성하라. 예를 들어 power(2,10)을 호출하면 1024를 계산해 리턴하면 된다. 정수 수준에서만 누승을 구하는 것이므로 오버플로우는 무시하기로 하고 효율도 고려치 않기로 한다. 표준 함수 중에 똑같은 계산을 하는 pow라는 함수가 이미 있지만 연습 삼아 만들어 보도록 하자.

#include <stdio.h>

// 두개의 정수 a와 b를 입력 받아

// a의 b승을 구하는 power함수 작성

// 계산된 값을 정수 수준으로 리턴

int power(int a, int b);

void main()

{

int i;

i=power(2,10);

printf("%d\n",i);

}

// 두개의 정수 a와 b를 입력 받아

int power(int a, int b)

{

int i;

int power=a;

// a의 b승을 구하는 power함수 작성

for(i=0; i<b-1; i++)

{

power=power*a;

}

// 계산된 값을 정수 수준으로 리턴

return power;

}

6.3 함수 호출 방식

6.3.1 값 호출

  • 인수

    호출원에서 함수에게 일을 시키기 위한 정보

    인수를 어떻게 전달하는가에 따라 값 호출(call by value)방식과 참조 호출(call by reference)방식이 있다.

  • 값 호출 방식(call by value)

    실인수의 값이 형식 인수로 전달되는 방식

  • 예제 CallValue

#include <stdio.h>

int plusone(int a);

void main()

{

int i,j;

i=5;

j=plusone(i);

printf("i=%d, 결과=%d\n",i,j);

}

int plusone(int a)

{

a=a+1;

return a;

}

j=plusone(i); // plusone함수에서 return 받은 값은 j에 대입

i는 바뀌지 않는다.

6.3.2 참조 호출

실인수의 번지를 전달하는 방식

  • 예제 CallRef

#include <stdio.h>

void plusref(int *a);

void main()

{

int i;

i=5;

plusref(&i);

printf("결과=%d\n",i);

}

void plusref(int *a)

{

*a=*a+1;

}

*a=*a+1;

*a는 a가 가리키고 있는 번지에 들어있는 값을 가리킨다.

a가 &i를 대입받았으므로 *a는 *(&i)라고 할 수 있다.

*(&i)는 곧 실인수 i와 같다.

값 호출 = 실인수로 값(Value)를 넘긴다는 뜻

참조 호출 = 번지값을 전달받아 실인수를 직접 참조(reference)할 수 있다는 뜻

  • 예제 ValueRef

#include <stdio.h>

void main()

{

int i,icopy;

int *pi;

i=5;

icopy=i;

icopy=icopy+1;

printf("i=%d, icopy=%d\n",i,icopy);

i=5;

pi=&i;

*pi=*pi+1;

printf("i=%d, &pi=%d\n",i,*pi);

}

6.3.3 출력용 인수

변수의 번지를 전달받아야 scanf 함수 내부에서 입력된 값을 변수에 대입해 줄 수 있다.

scanf("%d", &i)

&i 즉 i변수가 저장된 번지를 인수로 받았으므로 이 함수 내부에서 *i=x;식으로 i 변수에 값을 대입 할 수 있다.

문자열을 입력받을 때는 & 연산자를 사용하지 않는데 문자 배열은 그 자체가 포인터이기 때문이다.

인수는 언제 초기화되는지, 상수를 쓸 수 있는지에 따라 다음 세가지 형태로 분류

  • 입력용 인수

    가장 일반적인 의미의 인수

    호출원에서 함수에게 작업 대상을 지정하기 위해 전달

    plusone함수의 a인수, printf 함수의 모든 인수, gotoxy함수의 인수

  • 출력용 인수

    참조 호출로 전달되는 인수들의 대부분이 출력용 인수이다.

    함수에게 작업거리를 주기 위해 전달되는 것이 아니라 함수가 작업한 결과를 돌려받기 위해서 사용

    대표적으로 scanf 함수의 인수다.

    int i;

    scanf("%d", &i);

  • 입출력용 인수

    입력과 출력을 겸하는 인수

    호춸원에서 함수에게 작업 대상을 전달하기 위해서도 사용하며 함수가 호출원에게 작업 결과를 전달하기 위해서도 사용

    6.3.4 C++의 참조호출

6.4 전처리기

6.4.1 #include

#include와 같은 명령들을 전처리기라고 한다

전처리기는 말 그대로 "앞서 먼저 처리하는 명령"이라는 뜻인데 컴파일하기 전에 소스를 재작성하는 역할

컴파일러가 소스를 읽기 전에 전처리기가 먼저 실행되어 컴파일하기 좋도록 소스의 모양을 정리

  • #include <file.h>

    <>괄호를 사용하면 표준 헤더 파일 디렉토리에서 지정한 파일을 찾는다.

    컴파일러 옵션 중 표준 헤더파일의 경로를 지정 할 수 있음

  • #include "file.h"

    사용자가 직접 작성한 헤더 파일을 포함시키고자 할 때 " " 괄호를 사용

    소스파일과 같은 디렉토리에서 헤더파일을 먼저 찾는다.

    6.4.2 #define

    매크로 상수를 정의

  • 사용예

    #define 매크로명 실제값

  • 예제 define

#include "stdio.h"

#define MACH 1200.0

void main()

{

int speed;

printf("속도를 입력하세요(Km/h :");

scanf("%d", &speed);

printf("이 속도는 마하 %f입니다.\n",speed/MACH);

}

  • #define문으로 매크로 상수를 정의할 때 주의 사항

    1. #define문은 전처리문이지 코드를 생성하는 명령이 아니다.

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

    3. 매크로 이름에는 공백이 들어갈 수 없지만 매크로의 실제값은 공백을 가질 수 있다.

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

    5. 중첩 가능하다

    6. 값을 가지지 않는 빈 매크로도 정의할 수 있다.

    6.4.3 매크로 함수

    #define 전처리기를 사용하여 함수 흉내를 내는 것

    인수를 받아들일 수 있고 매크로 실행 후 계산 결과를 리턴한다는 면에서 함수와 유사하다.

  • 주의할 점

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

    2. 매크로의 인수들도 개별적으로 괄호를 싸준다.

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

    4. 여러 개의 명령을 동시에 포함 시킬 수 있다.

    5. 증감 연산자나 복합 대입 연산자는 쓰지 않는 것이 좋다.

  • 예제 MacroInc

#include <stdio.h>

#define dubae(i) ((i)+(i))

void main()

{

int k,j;

k=3;

j=dubae(k++);

printf("j=%d, k=%d\n",j,k);

}

6.4.5 C프로그램의 구조

#include <.....>

#define ....

함수의 원형

전역 변수

void main()

{

코드

}

함수

함수

반응형

'책정리 > 혼자 연구하는 C,C++ 1' 카테고리의 다른 글

8장 표준 함수  (0) 2015.02.19
7장 기억 부류  (0) 2015.02.19
5장 연산자  (0) 2015.02.19
4장 제어문  (0) 2015.02.18
3장 변수  (0) 2015.02.18