IT 과학/C언어

C언어 | 함수

곰뚱 2020. 1. 4.

 

 

 

C 프로그램은 함수의 모임으로 구성된다. 이제까지 printf나 scanf 등의 시스템이 제공하는 표준 라이브러리 함수를 사용해 왔지만, 본 내용은 사용자 함수를 만드는 방법에 관해서 설명한다. 또, 함수간의 인자 전달 방법으로서 call by value, call by reference란 개념과 배열의 전달 방법에 관해서도 상세히 설명한다.

 

1. 함수란?

(1) 함수와 리턴값

함수로서 가장 일반적인 것에 산술 함수가 있다. 예를 들면, sin(x)는 x의 싸인 값을 구하는 것이다. 그러나 C에서는 이러한 산술 함수 외에도 여러 가지 처리 단위를 함수로서 실행할 수 있다.

함수의 호출은 함수명( ); 로 실행하고 호출된 함수에서의 처리 결과가 리턴값으로서 함수명( )으로 되돌려진다.

 

(2) 함수의 정의

printf, scanf, sin, cos 등의 함수는 이미 컴파일러 제작자가 작성해 놓은 함수이므로 우리와 같은 사용자가 단순히 호출만 하면 되지만, 여기에서는 우리 나름대로 필요해서 쓰려고 하는 사용자 함수를 작성하는 법을 생각해 보자. 우선 간단한 예를 들어 보자.


        #include <stdio.h>

        #define NAME            "Lee, Hyolee"
        #define SCHOOL        "Department of Theater & Film, Kookmin Univ."
        #define ADDRESS       "861-1 Jeonreung-dong, Sungbuk-gu, Seoul 136-702, Korea"

        #define LINESIZE                65

        main()
        {
                starbar();
                puts(NAME);
                puts(SCHOOL);
                puts(ADDRESS);
                starbar();
        }

        starbar()
        {
                int count;

                for (count=0; count < LINESIZE; count++)
                        putchar('*');
                putchar('\n');
        }


확인문제 1

/* 간단한 기초 함수 프로그램의 예 */
#include <stdio.h>
/* 함수 main( )의 정의 */
main( )
{
        line( );
        printf("*  Lee  Hyolee *\n");
        line( );
}

/* 함수 line( )의 정의 */
line( )
{
        int j;
        for( j = 1; j <= 20; j++)
           printf("*");
        putchar('\n');
}

 

프로그램에서 함수를 어떻게 사용하는지 우선 함수의 전달 인수를 사용하지 않는 간단한 함수 프로그램을 작성하여 어떻게 함수가 호출되고 실행되는지 확인 문제 9-1의 간단한 예를 통해 다시 설명한다. 확인 문제 9-1은 ANSI C에서 권장하는 기본 형식을 따르지 않는 ANSI C 이전 형식의 프로그램이라는 것을 미리 밝힌다. (ANSI C 형식을 따라 프로그램을 작성하는 것이 바람직하다.)

 

확인 문제 1을 실행시켜 보기 바란다. 그 결과는 어떻게 될까?  이 문제는 main() 함수와 line()으로 구성된 프로그램이다. main() 함수에서 프로그램은 시작되고, 첫 번째 실행문에 의해서 함수 line()을 호출한다. 함수 line()이 호출되면 제어는 피호출 함수 line()으로 이동하는데, 이 때 운영체제는 함수 line()으로 이동하기 전에 함수 line()이 실행된 후 되돌아와서 실행될 다음 명령의 주소를 시스템 스택에 보관한다. 따라서 피호출 함수에 있는 모든 명령문이 실행되어 함수가 종료되면 이제 스택에 저장된 주소를 이용하여 다음 번 수행할 명령문의 위치로 제어를 이동시켜 나머지 명령문을 실행한다.

 

구체적으로는 프로그램은 main() 함수에서 시작되어 함수 호출문에 의해서 함수 line()을 호출하면 제어는 피호출 함수로 이동하여 함수 line()이 실행된다. 함수 line()은 for 문을 사용하여 printf() 함수를 20번 호출하여 '*' 문자를 20개 출력하고 나서, putchar() 매크로 함수를 사용하여 줄바꿈을 하고 종료된다. 함수가 종료되면 피호출 함수는 제어를 자신을 호출한 함수로 반납한다. 따라서 호출 main() 함수에서 첫번째 함수 호출문 다음에 있는 명령문인 printf() 함수가 실행된다. main() 함수의 마지막 문은 함수 호출문이므로 다시 함수 line()이 실행된 다음 함수 line()이 제어를 main()에 반납하면 main() 함수에서는 함수의 끝에 도착했기 때문에 프로그램을 종료한다.

 

앞의 문제를 통해 함수가 어떻게 실행되는지 보았다. 어떤 함수를 실행하기 위해서는 프로그램 내부에 명확한 함수 호출문이 있어야 한다. 이 때 함수 호출문에서 사용하는 함수명은 함수 정의부에서 사용한 함수명과 동일해야 하며, 함수 호출문의 일반적인 형식은 다음과 같다.

 

함수명([실 인수 리스트]);
   

위의 형식에서 대괄호를 사용하여 묶인 부분은 생략할 수 있다는 뜻이다. 즉, 함수 정의부에 있는 형식 인수로 데이터를 전달할 실 인수가 없을 때는 공백인 상태로 두면 된다. 이 때 주의할 것은 실 인수의 유/무와 상관없이 한 쌍의 소괄호와 호출문의 끝을 나타내는 세미콜론은 반드시 있어야 한다. 그리고 하나 이상의 실 인수가 사용되면 순서 연산자(',')에 의해서 실 인수를 서로 분리해야 한다.

 

다음으로 함수 프로토타입(prototype) 즉, 원형과 void 형 함수의 기초에 대해 설명한다. ANSI C에서는 함수도 변수처럼 함수를 사용하기에 앞서 함수를 선언하도록 권장하고 있다. 함수 선언을 했을 때 얻을 수 있는 장점은 여러 가지가 있다. 여기서는 함수 선언을 어떻게 하고 void 형 함수란 무엇인지에 대해서만 알아본다.

 

함수의 선언은 프로그램에서 사용되는 함수의 원형을 사용 전에 미리 선언하는 것이다. 함수의 선언을 함수 원형(prototype) 선언이라고도 하며, 함수 원형 선언의 형식은 다음과 같다.

 

반환형 함수명(void 또는 각 전달 인수의 형);

 

만약 하나 이상의 전달 인수가 사용된다면 각 전달 인수의 자료형을 순서 연산자(',')를 사용하여 분리하면 되고, 전달 인수가 사용되지 않는다면 void를 사용하여 전달 인수가 없다는 것을 나타내면 된다. 함수 원형 선언은 호출 함수와 피호출 함수간의 연결(interface)을 어떻게 할 것인가를 나타낸다. 즉, 함수 원형은 함수 정의부에 기술된 함수에 대한 모든 정보를 컴파일러에게 알려준다. 컴파일러는 함수 원형 선언을 통하여 얻는 정보를 갖고 있으므로 함수의 호출부와 정의부간에 전달되는 인수의 자료형이나 개수가 다른 경우나 반환형이 다른 경우에 함수 원형 선언에서 얻은 정보를 이용하여 이를 오류로 처리한다. 따라서 전달 인수의 자료형이나 개수, 반환형이 서로 일치하지 않을 때 발생하는 모든 논리적 오류를 사전에 예방할 수 있다.

 

C 언어에서 사용하는 모든 함수는 함수의 반환형을 지정하지 않으면 int형으로 내정된다. 즉, 자동적으로 함수의 반환형은 정수형으로 결정된다는 것이다. 따라서 피호출 함수가 실행된 후 실행 결과를 호출 함수에게 반환하지 않는 함수라 할지라도 반환형은 int형이 되며, 피호출 함수는 알지 못하는 int형 데이터를 반환형으로 갖게 되지만 내정된 반환형이 int형이므로 아무런 문제없이 오류가 발생하지 않는다.

 

ANSI C 표준안에서는 이와 같은 모순을 해결하고자 void형 함수를 제안했다. 즉, 호출 함수로 값을 반환하지 않는다면 함수의 정의부에서 반환형을 지정할 때 이를 void형으로 지정하여 피호출 함수가 예측할 수 없는 int형 데이터를 반환형으로 갖지 않도록 한다. 그리고 ANSI C에서는 호출 함수와 피호출 함수간에 전달 인수를 사용하지 않는다면 전달 인수가 없음을 명확히 하고, 만약 잘못된 전달 인수가 사용된다면 이를 찾아내기 위해서 함수의 원형 선언부와 정의부의 전달 인수 목록에 void를 사용하도록 제안하고 있다. 이제 확인 문제 9-1을 ANSI C 표준안에 따라서 다시 작성하면 확인 문제 9-2와 같다. 확인 문제 9-1과 어떻게 다른가 자세히 살펴보기 바란다.

 

확인문제 2

/* ANSI C 표준안을 사용한 간단한 기초 함수 프로그램의 예 */
#include <stdio.h>
/* 함수 main( )의 정의 */
void main(void)
{
        void line(void);
        line( );
        printf("*  Lee  Hyolee *\n");
        line( );
}

/* 함수 line( )의 정의 */
void line(void)
{
        int j;
        for( j = 1; j <= 20; j++)
           printf("*");
        putchar('\n');
}

 

확인 문제 2에서는 함수 line()이 모두 4번 나타난다. 가장 첫번째 나타나는 것은 함수 line()의 원형 선언부이다. 원형 선언에서 함수 line()은 호출 함수로 반환되는 값이 없음을 나타내고 있고, 전달 인수도 역시 없다는 것이다. 그리고 원형 선언은 선언문이므로 반드시 선언문 끝에 세미콜론이 필요하다. 두번째, 세번째는 모두 함수 line()의 호출부이다. 함수의 호출은 함수명에 의해서 호출되므로 함수명 앞에 void를 사용해서는 안되고, 실 인수가 없다고 할지라도 void를 사용해서는 안된다. 만약 실 인수가 없으면 공백으로 비워두면 된다. 네번째는 함수 정의부이다. 함수 정의부는 함수 원형 선언부와 동일하지만 정의부이므로 함수의 헤더(머리) 끝에 세미콜론을 붙여서는 안된다. 컴파일러는 함수의 원형 선언과 정의부의 구분을 세미콜론 유/무에 따라 구분하므로 원형 선언에서는 세미콜론을 붙이지만 정의부의 헤더에서는 세미콜론을 붙이면 안된다는 것을 명심하기 바란다. 그리고 지금까지 사용된 main() 함수도 ANSI C 표준안에 따르기 위해서 함수의 반환형과 전달 인수를 사용하지 않는다면 다음과 같이 void를 사용하는 것이 바람직하다.

 

void main(void)

 

지금까지 우리는 main() 함수를 사용할 때 ANSI C 표준안을 따르지 않았지만 앞으로는 ANSI C 표준안에 따라 main() 함수를 구성하기로 한다. 그렇다면 main() 함수도 원형 선언이 필요할까? 물론 main() 함수의 원형을 선언해도 상관 없지만 사용하지 않는 것이 일반적인 관례이다. 그 이유는 main() 함수는 프로그램을 시작하는 함수이므로 main() 함수의 원형에 대한 정보를 프로그램이 시작할 때 이미 컴파일러가 알고 있기 때문이다.

 

이처럼  C에서 함수의 호출은 단순히 「함수명( );」으로 표시하면 되고, call이나 gosub을 붙이지 않는다. 함수명 다음의 ( )내에는 함수로 전달되는 데이터(이것을 실인수(actual argument)라 함)를 넣는다.

 

이것에 대해서 함수쪽에서는 실인수의 데이터를 받아들이는 변수(이것을 형식인수(formal argument)라 함)를 쓴다. 실인수에서는 상수, 변수, 식 등을 쓰지만 형식인수는 데이터를 받아들이는 그릇에 불과하므로 변수만 사용해야 한다. 인수 사이의 구분은 콤마로 하지만 없으면 생략해도 된다. 또 실인수와 형식인수의 데이터형은 일치해야 한다.

 

함수의 종결은 } 또는 return문에 의하여 호출시 리턴된다. 이 때 return문 식의 값이 함수의 리턴값으로 되돌려진다. 함수는 그 함수가 리턴하는 값에 대응하는 형을 갖게 되어 있으므로 함수형을 생략하면 int 형의 값을 리턴하는 함수가 된다.

 

(3) 함수에 있어서의 변수

C에서의 변수는 변수가 선언되는 함수 내에서만 유효하다. 이런 변수를 지역 변수 (local variable)라고 부른다. 아래의 예에서 main과 sub의 변수 i는 동일한 이름이라고 하더라도 별개의 것이며 서로 영향을 미치지는 않는다. 즉 sub에서 i의 값을 변화시켜도 main의 i는 전혀 영향을 받지 않는다. 이와 같이 지역 변수는 함수를 모듈화시켜서 독립성을 높이는데 중요한 역할을 한다. 이에 반해 전역변수(global variable)는 함수 밖에서 선언된 변수로 변수가 선언된 이후의 모든 함수에서 사용 가능한 함수이다.


          main( )
          {    
             int i;
             for(i=0, u<=10, i++)
             sub();
          }

          sub( )
          {  
              int i;
              i = 5;
           }  

728x90

 

 

2. 함수간에 데이터를 주고 받는 방법

확인문제 3

/* 실인수와 형식인수의 사용의 예 */
#define DOUBLE_LINE "\xCD"
#include <stdio.h>
void bar(int);
void main(void)
{
      int score = 1;

      while( printf("Score = "), scanf("%d", &score) == 1)
      {
        if(score <= 0 || score >= 30) break;
        bar(score);
      }
}

void bar(int count)
{
      int i;

      for(i = 1; i <= count; i++)  /* count의 크기만큼 이중 선을 그린다 */
        printf(DOUBLE_LINE);
      printf("\n");                /* 그래프의 끝에서 줄 바꿈 */
}

 

확인문제 4

/* 사각형을 그리는 프로그램 (한 개 이상의 전달 인수를 사용하는 예) */
#include <stdio.h>
void rectangle (int, int);
void main(void)
{
      int width, length, count;

      printf("Input length and width: ");
      count = 1;
      while(scanf("%d %d", &length, &width) == 2 && count <= 5)
      {
          printf("count = %d\n", count);
          rectangle(length, width);
          count++;
          printf("Input length and whdth: ");
      }
}

void rectangle(int length, int width)
{
      int i, j;

      for( i = 1; i <= width; i++)
      {
          printf("\t\t");
          for( j = 1; j <= length; j++)
              printf("*");
          printf("\n");
      }
}

 

(1) 값에 의한 호출 (call by value)

값에 의한 호출은 C에 있어서 가장 일반적인 인수 전달 방법으로 실인수의 실제값을 형식인수로 전달해 준다. 그러나 형식인수의 값은 실인수에 되돌려지지 않으므로 인수에 의한 데이터를 주고받는 방법은 「실인수→형식인수」의 일방 통행으로 된다. 이 때문에 호출된 함수쪽에서 잘못으로 인수의 값을 변경했다고 해도 호출함수에는 영향을 주지 않기 때문에 두 개의 개별적 모듈간의 독립성은 극대화된다.

 

확인문제 5

/* 값에 의한 호출의 기초 예제 프로그램 */
#include <stdio.h>
int sub(int);
void main(void)
{
      int data = 10;
      

      printf("A value  of Calling function: main( ) ==> %5d\n", data);
      printf("A result of Called function :  sub( ) ==> %5d\n", sub(data));
      printf("A value  of Calling function: main( ) ==> %5d\n", data);
}

int sub(int data)
{
      data = data * data;
      printf("A value  of Called function :  sub( ) ==> %5d\n", data);
      return data;
}

 

(2) 함수값을 리턴(return 문)

값 하나만을 리턴하는 함수가 C에 있어서 가장 일반적인 함수이다. 함수에서 값을 리턴하려고 할 때에는 아래와 같이 return 문을 사용한다.

 

return(식);

 

여기에서 식의 값이 함수의 리턴값으로 자신을 호출한 곳으로 되돌려진다. 이 때에 리턴식의 값과 함수형의 값은 일치해야 한다.

 

예제 1. 절대값을 구하는 함수 abs( )를 작성하여라.

●프로그램   
#include <stdio.h>
int abs(int x); /*프로로타입(원형) */

main( )
{   
           int a;
           while(scanf("%d", &a)!=EOF){
               printf("abs=%d\n",abs(a)); /* 함수 호출 */
           }
}

int abs(int x) /* 함수 정의 */
{
     if (x>=0)
        return (x);
     else
        return (-x);
}

 

● 결과
  35
  abs=35
  -76
  abs=76
  0
  abs=0
  ^z

 

확인문제 6

/* 반환 값을 갖지 않는 return문의 사용의 예 */
#include <stdio.h>
void main(void)
{
        void sum(void);
        printf("Input two int data: ");
        sum( );
        printf("program end!!!\n");
}

void sum(void)
{
        int  count;
        long data1, data2, result;

        scanf("%ld %ld", &data1, &data2);
        count = 1;
        while( count++ <= 10 )
        {
            result = data1 + data2;
            if( result > 32769 || result < -32769)
            {
                printf("Out of Integer!!\n");
                return;
            }
            printf("result = %ld\n", result);
            printf("Input two int data: ");
            scanf("%ld %ld", &data1, &data2);
        }
}

 

확인문제 7

/* 계산된 결과 값을 반환하는 return 문의 사용의 예 */
#include <stdio.h>
void main(void)
{
      char get_ch(void);

      printf("Type 'a' for first selection, 'b' for second selection: ");
      switch( get_ch( ))
      {
        case 'a':
            printf("\nYou typed an 'a'.");
            break;
        case 'b':
            printf("\nYou typed a 'b'.");
            break;
        default :
            printf("\nYou chose a non-existent selection.");
      }
}

char get_ch(void)
{
      char ch;
      ch = getche( );
      if ( ch >= 65 && ch <= 92)
           ch = ch + 32;
      return ch;
}

 

확인문제 8

/* 시간의 차를 계산하여 반환하는 int 형 함수의 예제 프로그램 */
#include <stdio.h>
void main(void)
{
      int mins1, mins2;
      int get_time(void);

      printf("Type first time (form 3:22): ");
      mins1 = get_time( );
      printf("Type second(later) time: ");
      mins2 = get_time( );
      printf("Difference is %d minutes.", mins2 - mins1);
}

int get_time(void)
{
      int hours, minutes;

      scanf("%d:%d", &hours, &minutes);
      return (hours * 60 + minutes);
}

 

응용문제 1: 두 실수의 합을 계산하여 반환하는 함수 plus(x, y)와 xy를 계산하여 결과를 반환하는 함수 power(x, y)를 작성하라.

 

예제 2: n개의 정수를 읽어서 합, 평균, 가장 큰 값을 구하는 프로그램

        #include <stdio.h>

        #define MAXDATA 100

        long SumOfData(int [], int);
        void InputData(int [], int);
        void PrintData(int [], int);
        int HighestData(int [], int);

        void main(void)  /* sum, average, highest data */
        {
                int n, data[MAXDATA];
                long sum, highest;
                float average;

                printf("Input number of data : ");
                scanf("%d", &n);
                InputData(data, n);
                PrintData(data, n);

                sum = SumOfData(data, n);
                average = (float) sum / (float) n;
                highest = HighestData(data, n);

                printf("sum = %ld, average = %.2f\n", sum, average);
                printf("highest data = %d\n", highest);
        }

        void InputData(int data[], int n)              /* n개의 data를 입력 */
        {
                int i;

                for (i = 0; i < n; i++)
                        scanf("%d", &data[i]);
        }

        void PrintData(int data[], int n)              /* 입력된 n개의 data를 출력 */
        {
                int i;

                printf("Data = ");
                for (i = 0; i < n; i++)
                        printf("%5d ", data[i]);
                putchar('\n');
        }

        long SumOfData(int data[], int n) /* n개의 data의 합 */
        {
                int i;

                long total=0;

                for (i = 0; i < n; i++)
                        total += data[i];
                return total;
        }

        int HighestData(int data[], int n)        /* n개의 data 중에서 가장 큰 것 */
        {
                int i, max;

                max = data[0];
                for (i = 0; i < n; i++)
                        if (data[i] > max) max = data[i];
                return max;
        }

 

(3) 참조에 의한 호출(call by reference)

함수는 문법 구조상 리턴값을 하나 밖에 가지지 못한다. 그러나 둘 이상의 값을 리턴해야 하는 경우도 있다. 이와 같은 경우에는 함수의 리턴값으로 한 번에 두 개의 값을 리턴하는 것이 아니고 포인터를 사용해서 인수를 전달하여 실행하고, 호출된 함수에서의 결과를 인수로 호출원으로 돌려준다. C 언어는 참조에  의한 호출이 가능하지 않으므로 이와 같은 방법으로 마치 참조에 의한 호출 (call by reference)  방식과 같이 실행되게 한다.

 

다음의 예에서는 함수 호출 때에 실인수 a, b 의 주소가 형식 인수 x, y 로 전달된다.

 

func(&a, &b); /* 함수 호출 */

func(int *x, int *y) /*  함수 정의 */
{ ...

}

따라서 실인수 a 의 주소가 만약 0x2000이면 형식인수 x 에는 0x2000이 전달된다. 인수 x는 포인터 변수로서 선언되어 *x 에 의하여 호출 쪽의 인수 a 의 내용을 참조하게 된다. 즉 함수 정의 쪽에서

 

*x = 100;

 

라 하면 함수 호출 쪽의 실인수 a의 내용이 100으로 변화된다.

 

이와 같이 참조에 의한 호출에서는 함수 쪽에서 호출 쪽의 인수를 참조할 수 있기 때문에 값에 의한 호출(call by value) 에 비교해서 모듈간의 독립성은 떨어진다. 그러나 두 개 이상의 리턴값이 필요할 경우와 배열 데이터를 전달할 경우에는 참조에 의한 호출을 사용하지 않으면 안 된다.

 

예제 3. 데이터 a, b의 덧셈, 뺄셈을 구하는 함수를 작성하여라.

● 프로그램

#include <stdio.h>
void calculation(int w, int x, int *y, int *z); /*함수 프로토타입 */

main()
{
       int a, b, c, d;
       scanf("%d %d", &a, &b);
       calculation(a, b, &c, &d); /* 함수 호출 */
       printf("%d + %d = %d\n", a, b, c);
       printf("%d - %d = %d\n", a, b, d);
}
 
void calculation (int w, int x, int *y, int *z) /* 함수 정의 */
{
      *y = w + x;            
      *z = w - x;               
 }

● 결과
       55 6     

       55 + 6 = 61     
       55 - 6 = 49      

 

확인문제 9

/* 참조에 의한 호출을 사용하여 두 변수의 값을 서로 교환해 주는 예제 */
#include <stdio.h>
void swap(int *, int *);
void main(void)
{
      int i = 55, j = 77;

      printf("Original : %d  and  %d\n", i, j);
      swap(&i, &j);
      printf("     New : %d  and  %d\n", i, j);
}

void swap(int *x, int *y)
{
      int temp;

      temp = *x;
      *x = *y;
      *y = temp;
}

 

 

3. 배열 데이터 전달 방법

1차원 배열, 2차원 배열, 포인터 배열 등의 배열을 함수로 전달하려면 참조에 의한 호출(call by reference)을 사용한다. 즉 배열의 모든 데이터를 함수로 전달하는 것이 아니라 배열의 시작 주소만을 전달하고 나머지는 함수 쪽에서 포인터를 사용하여 각 배열 요소를 참조한다.

(1) 1차원 배열의 전달

         int a[100];

라는 배열을 함수에 전달하려면,

 

func(a); /* 함수 호출 */

func(int b[]) /*  함수 정의 */
{ ...

}

 

로 표시하면 된다. 여기에서 func(a);는 함수 호출,  func(b)는 함수 정의이다. 배열명 a는 배열의 시작 주소를 나타내는 것이기 때문에 이것을 실인수로 하여 전달된다. 형식 인수 b는 배열의 실체를 받는 것이 아니기 때문에 int b[ ]; 와 같이 선언하고 [ ] 내에는 값을 쓰지 않는다. 또는 int b[ ]; 대신에 int *b;로 해도 모두 같은 것이다.

또한 함수 정의 쪽에서 b[0] ~ b[99]로 하는 것은 함수 호출쪽의 배열 a[0] ~ a[99]를 참조하는 것이다.

 

예제 4. 1차원 배열 a[ ] 의 각 요소의 합을 구하는 함수 isum을 작성하여라.

● 프로그램
#include <stdio.h>
int isum(int b[]);
 
main()
{
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -999};             
    printf("배열 a의 합은 %d\n", isum(a));
}

int isum(int b[])
{
    int s, i;                   
    s = i = 0;                       
    while (b[i] != -999)
           s = s + b[i++];        
    return(s);
}     

 

● 해설

배열 a의 합계가 isum(a)에 의하여 구해진다. 데이터의 종결은 -999, 합계를 s에 대입, b[i]로  main의 a[i]를 참조한다. 구한 합계 s를 리턴값으로 사용한다.

 

● 결과
배열 a의 합은 55         

  

확인문제 10

/* 배열 array에 저장된 모든 데이타의 합을 구하는 예제 프로그램 */
#include <stdio.h>
long sum_1(int *pt, int size);
long sum_2(int pt[ ], int size);
void main(void)
{
      static int array[ ] = {10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60};
      long total;

      clrscr( );
      total = sum_1(array, sizeof(array) / sizeof(int));
      printf("The total value of array is %ld\n", total);
      putchar('\n');
      printf("The size of array is %d bytes.\n", sizeof(array));
      putchar('\n');
      total = sum_2(array+5, (sizeof(array)/sizeof(int))-5);
      printf("The total value of array is %ld\n", total);
}

long sum_1(int pt[ ], int size)
{
      int index;
      long result = 0;

      printf("===<Result of sum_1( )>===\n");
      for(index = 0; index < size; index++)
      result += *(pt + index);
      printf("The size of pt is %d\n", sizeof(pt));
      return(result);
}

long sum_2(int *pt, int size)
{
      int index;
      long result = 0;

      printf("===<Result of sum_2( )>===\n");
      for(index = 0; index < size; index++)
        result += pt[index];
      return(result);
}

 

(2) 2차원 배열 전달

int a[5][10];

라 하는 2차원 배열을 전달하려면

 

func(a); /* 함수 호출 */

func(int b[][10]) /*  함수 정의 */
{ ...

}

 

라고 표시한다.

 

1차원 배열의 인수를 전달하는 경우와 동일하므로 형식 인수 b는 포인터이다. 그러나 2차원 배열에서는 요소의 위치를 계산하는데 열 요소의 크기를 알고 있어야 하므로 int b[ ][10]; 와 같이 열 요소의 크기를 선언해야 한다. 함수 정의 쪽에서는 b[i][j] 에 의하여 함수 호출 쪽의 a[i][j]를 참조하는 것이다.

 

예제 5. M행 N열의 배열을 초기화하는 함수 setw( )를 작성하여라.

● 프로그램

#include <stdio.h>
#define M 5
#define N 8
void setw(int b[][N], int dat);
 
main()    
{       
   int a[M][N], j, k;
   setw(a, 0);                                  
   for (j=0; j<M; j++) {
         for (k=0; k<N; k++)
                printf("%2d", a[j][k]);
                printf("\n");
         }
}

void setw(int b[][N], int dat)
{
   int j, k;
   for (j=0; j<M; j++)
         for (k=0; k<N; k++)
                b[j][k] = dat;
}

 

● 결과
    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0

 

그러나 예제 9-5와 같이 형식인수를 int b[ ][N]; 와 같이 선언했을 경우 함수 setw에 N이라는 고정된 값이 들어가게 되어 함수로서의 범용성이 없어지게 된다.

 

다음과 같이 선두 주소와 행의 수, 열의 수를 전달하는 방법도 생각해 보자.

 

           int a[m][n];
           setw(a, m, n, dat);

           void setw(int *b, int j, int k, int dat)
           {
               
           }
      

위와 같이 표시하면 2차원 배열 a[][] 는 포인터 b를 사용해서 선형으로 참조가 되는 것이다.

 

예제 6. 예제9-5의 setw( )를 개선하여 인수 선언에 N이 들어가지 않도록 작성하여라.

● 프로그램
#include <stdio.h>
void setw (int *b, int m, int n, int dat);

main()
{
   int a[5][8], j, k;
   setw(a, 5, 8, 0);
   for (j=0; j<5; j++) {
           for (k=0; k<8; k++)  
                printf("%2d", a[j][k]);
                printf("\n");
           }
}
 
void setw (int *b, int m, int n, int dat)
{
    int k;                               
    for (k=0; k<m*n; k++)                         
           *b++ = dat;                               
}                                                    

 

● 해설

배열 a[][]는 5행 8열로 된 것을 함수에 전달하고 배열의 요소 개수는 m×n 개이므로 이 횟수만큼 초기화한다. 또한 *b++=dat;는 *b=dat; b++; 과 같다.

 

확인문제 11

/* 2차원 배열의 부분 배열에 1차원 배열 포인터를 적용 */
#include <stdio.h>
void power(int *, int);
void main(void)
{
      int array[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
      int i, j;

      for(i = 0; i < 3; i++)
         power(array[i], 3);

      /* 연산 결과 출력 */
      for(i = 0; i < 3; i++)
      {
        for(j = 0; j < 3; j++)
            printf("%5d", array[i][j]);
        putchar('\n');
      }
}

void power(int *pt, int size)
{
      int i;

      for(i = 0; i < size; i++)
        *(pt + i) *= *(pt + i);
}

 

확인문제 12

/* 1차원 배열 포인터를 다차원 배열에 적용 */
#include <stdio.h>
void power(int *, int);
void main(void)
{
      int array[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
      int i, j;

      power(&array[0][0], 3 * 3);
      for(i = 0; i < 3; i++)
      {
        for(j = 0; j < 3; j++)
            printf("%5d", array[i][j]);
        putchar('\n');
      }
}

void power(int *pt, int size)
{
      int i;

      for(i = 0; i < size; i++)
        pt[i] *= pt[i];
}

 

확인문제 13

/* 1차원 배열 포인터를 사용하여 2차원 배열에 접근 */
#include <stdio.h>
void power(int *, int);
void main(void)
{
      int array[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
      int i, j;

      clrscr( );

      power(&array[0][0], 3);

      for(i = 0; i < 3; i++)
      {
        for(j = 0; j < 3; j++)
            printf("%5d", array[i][j]);
        printf("\n\n");
      }
}

void power(int *pt, int size)
{
      int i, j;

      for(i = 0; i < size; i++)
        for( j = 0; j < 3; j++)
            *(pt + ((i * 3) + j)) *= *(pt + ((i * 3) + j));
}

 

확인문제 14

/* 다차원 배열 포인터에 다차원 배열을 적용 */
#include <stdio.h>
void power(int (*)[3], int);
void main(void)
{
      int array[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
      int i, j;

      power(array, 3);
      for(i = 0; i < 3; i++)
      {
        for(j = 0; j < 3; j++)
            printf("%5d", array[i][j]);
        putchar('\n');
      }
}

void power(int pt[ ][3], int size)
{
      int i, j;

      for(i = 0; i < size; i++)
        for(j = 0; j < 3; j++)
            pt[i][j] *= *(*(pt + i) + j);
}

 

(3) 포인터 배열의 전달

char *name[10];

와 같은 포인터 배열을 함수로 전달하려면 1차원 배열과 동일 방법으로 다음과 같이 표시한다.

 

func(name); /* 함수 호출 */

func(char *p[]) /*  함수 정의 */
{ ...

}

 

예제 7. 이름을 데이터로 표시하는 함수 stars를 작성하고, 데이터의 종결은 '/'로 하여라.

● 프로그램

#include <stdio.h>
void stars(char *singer[]);

main()
{
   char *finkl[]={"Hyolee", "Joohyun", "Yoori", "Leejin", "Hyori", "/"};
   stars(finkl);
}

void stars(char *singer[])
{
    int i = 0;
    while (singer[i][0] != '/') {
             printf("%s\n", singer[i]);
             i++;
    }
}

 

● 해설

문자열의 첫번째 문자는 singer[i][0]에서 참조된다.

 

● 결과

 Hyolee

 Joohyun

 Yoori

 Leejin

 Hyori

 

 

4. 함수형

(1) 함수형의 선언

함수형이란 함수의 형님(brother)이 아니라 함수가 리턴하는 값의 타입(type)을 말한다. 함수형이 별도로 선언되지 않으면 int 형으로 가정한다. 실수형이나 포인터형 등의 int 형 이외의 리턴값 함수는 호출과 정의 쪽에서 각각 그 형을 선언하여야 한다.

 

예를 들어, double 형의 리턴값 함수 func( )는 호출 쪽으로 다음과 같이 선언한다.

 

  main( )
  {
        double func();    /* 함수 호출 */
  }
  

  double func(double x)    /* 함수 정의 */
  {
                
        return(double형의 식);
  }

 

우선, func( )를 호출하여 함수 쪽으로 선언한다. 이 선언에서는 ( )내에 인수를 쓰지 않는다. 또 함수의 정의부라도 같은 형으로 선언한다. 그리고 return에 의해서 리턴값도 선언한 함수형과 일치한다.  또는 main( ) 함수의 밖에서

 

       double func( );
        main( )
        {
                
        }

 

라고 선언되어 있으면, func( )를 사용하는 함수마다 함수형을 선언할 필요는 없다.

 

확인문제 15

/* ANSI C 이전의 원형 선언의 문제점 */
#include <stdio.h>
int max( );
void main(void)
{
     printf("The maximum of %d and %d is %d.\n",
           55, 77, max(55));
     printf("The maximum of %d and %d is %d.\n",
           55, 77, max(55.0, 77.0, 44.0));
}

int max(i, j)
int i, j;
{
      return (i > j)? i : j;
}

 

확인문제 16

/* ANSI C 표준안에 따른 함수 원형의 선언 */
#include <stdio.h>
int max(int, int);
void main(void)
{
     printf("The maximum of %d and %d is %d.\n",
           55, 77, max(55, 77));
     printf("The maximum of %d and %d is %d.\n",
           55, 77, max(55.0, 77.0));
}


int max(int i, int j)
{
      return (i > j)? i : j;
}

 

예제 8. 주어진 문자열을 반전시키고, 그것의 포인터를 리턴하는 함수 reverse( )를 작성하여라.

● 프로그램

#include <stdio.h>
#include <string.h>
char *reverse(char a[]);
char buf[80];
 
main( )
{
        char str[80], *reverse();
        scanf("%s", str);
        printf("%s\n", reverse(str));
}

char *reverse(char a[])
{
        int i, n;
        n = strlen(a);
        for (i=0; i<n; i++)
                buf[i]=a[n-i-1];
        buf[n]='\0';
        return(buf);
}

 

● 결과
 Hyolee
 eeloyH

 

(2) void형

함수의 특별한 형으로써 void가 있는데, 이것은 ANSI 규격안에 준하는 것으로 리턴값이 없는 함수형이다. 종래에는 리턴값을 갖지 않는 함수도 int 형으로 혼동하여 사용하였지만, void형을 이용하여 엄밀한 형 선언을 하려고 하는 것이다. 예를 들면 다음과 같다.

 

 void spc( );
 void main( )
 {
        int i ;
        for (i=0; i<5; i++) {
                spc(i) ;
                printf("Hyolee\n");
        }
 }

 void spc(int n)
 {
        int i;
        for (i=0; i<n; i++)
                printf("  ");
 }

 

main( ) 앞에서 void spc( );라고 선언하지 않으려면, 다음과 같이 그 함수를 호출하는 함수 앞에서 정의하면 된다.

 

 void spc(int n)
 {
        int i;
        for (i=0; i<n; i++)
                printf("  ");
 }

 void main( )
 {
        int i ;
        for (i=0; i<5; i++)
                spc(i);
                printf("Hyolee\n");
        }
 }

 

 

5. main 함수의 인수(명령행 인수)

지금까지의 프로그램은 main 함수에는 인수를 쓰지 않았다. 그러나 실제로 main 함수에 인수를 쓸 수 있는데, 다음과 같이 명령행에서 인수를 받아 쓸 수 있다.

 

        main(int argc, char *argv[])
        {
                
        }

 

argc에는 명령행으로 사용되는 인수의 수가 전달되고,  argv[0], argv[1]… 에는 각 인수의 포인터가 전달된다. 가령 이 프로그램을 mcopy라는 이름으로 작성하고 명령행에서 실행했다면 각 인수(공백에 의하여 구분되어져 있다)는 다음과 같이 main 함수에 전달된다.

 

        A:>mcopy  abc.txt  aaa.txt

        argc : 3
        argv[0] : mcopy\0
        argv[1] : abc.txt\0
        argv[2] : aaa.txt\0

 

예제 9. 정수형 데이터가 들어갈 배열을 전달하고 오름차순으로 정렬하는 함수 isort( )를 작성하여라. a[ ]에 데이터가 들어갈 배열, n을 데이터의 개수로서 isort(n, a)를 호출하라.

 

● 프로그램

#include <stdio.h>
void isort(int n, int a[]);
 
main( )
{
        int a[10]={5, 8, 4, 10, 50, 60, 33, 3, 5, 7};
    int i;
    isort(10, a);
    for (i=0; i<10; i++) {
           printf("%4d", a[i]);
    }
        printf("\n");
}

void isort(int n, int a[])
{
        int k, l, s, min, dumy;
    for (k=0; k<n-1; k++) {
           min = a[k];
           s = k;
           for (l=k; l<n; l++) {
                  if (a[l] < min) {
                        min = a[l];
                        s = l;
                  }
            }
            dumy = a[k];
            a[k] = a[s];
            a[s] = dumy;
     }
}

 

● 결과

  3  4  5  5  7  8  10  33  50  60

 

예제 10. 문자열의 임의의 위치에서 임의의 길이를 문자열로 표시하는 함수 mid( )를 작성하여라. 문자열 ss의 a번째의 문자부터 b개의 문자열을 꺼내고 ds에 저장하는 함수를 mid(ds, ss, a, b) 라고 하라.

 

●프로그램

#include <stdio.h>
#include <string.h>
void mid(char *ds, char *ss, int a, int b);

main( )
{
        char *name, result[255];
        unsigned int k;
        name = "Hyolee";
        for (k=1; k<=strlen(name); k++) {
                mid(result, name, 1, k);
                printf("%s\n", result) ;
        }
}

void mid(char *ds, char *ss, int a, int b)
{
        int k, n;
        n=strlen(ss);
        if ((0<a && a<=n) && (0<b && b<=n) && (a+b-1<=n)) {
                for (k=a; k<a+b; k++)
                        ds[k-a]=ss[k-1];
                ds[b]='\0';
        }
        else
                ds[0]='\0';
}

 

● 결과
 H
 Hy
 Hyo
 Hyol
 Hyole
 Hyolee

 

확인문제 17

/* main( ) 함수의 명령행 전달 인수를 역순으로 출력하는 프로그램 */
#include <stdio.h>
void reverse_print(char *);
int main(int argc, char *argv[ ])
{
      int i;

      for(i = argc-1; i >= 0; i--)
      {
          printf("argv[%d] = %s\n", i, argv[i]);
          printf("Reverse Data: ");
          reverse_print(argv[i]);
      }
      return(0);
}
void reverse_print(char *spt)
{
      int i, size;

      size = strlen(spt);
      for(i = size; i >= 0; i--)
          putchar(spt[i]);
      putchar('\n');
}

 

 

6. 순환(재귀) 함수 (Recursive Function)

확인문제 18

/* 직접 순환 함수의 작성 */
#include <stdio.h>
void recursion(int);
int main(void)
{
      recursion(1);
      return 0;
}

void recursion(int n)    /* 순환 함수 recursion( ) */
{
      printf("level: %d\n", n);    /* 첫번째 출력문  */
      if (n < 4)
         recursion(n + 1);         /* 순환 함수 호출 */
      printf("\tLEVEL: %d\n", n);  /* 두번째 출력문  */
}

 

확인문제 19

/* factorial 순환 함수 프로그램 */
#include <stdio.h>
long fact(int);
void main(void)
{
      int n;
      printf("Input integer: ");
      while(scanf("%d", &n) == 1)
      {
          printf("%d! = %ld\n", n, fact(n));
          printf("Input integer: ");
      }
}

long fact(int n)
{
      if(n > 0)
          return(n * fact(n - 1));
      else
          return 1;
}

 

확인문제 20

/* 반복문을 사용한 factorial 프로그램 */
#include <stdio.h>
long fact(int);
void main(void)
{
      int n;
      printf("Input integer: ");
      while(scanf("%d", &n) == 1)
      {
          printf("%d! = %ld\n", n, fact(n));
          printf("Input integer: ");
      }
}

long fact(int n)
{
      long i, result;

      result = 1;
      if(n > 0)
      {
          for(i = 2; i <= n; i++)
              result *= i;
          return result;
      }
      else
          return 1;
}

 

확인문제 21

/* 참조에 의한 호출을 사용한 순환 함수 */
#include <stdio.h>
int sum(int *);
void main(void)
{
     int n;

     printf("Input integer: ");
     while(scanf("%d", &n) == 1)
     {
         printf("sum = %d\n", sum(&n));
         printf("Input integer: ");
     }
}

int sum(int *q)
{
      int a;

      if( *q <= 1 ) return *q;
      else
      {
          a = *q - 1;
          return( *q + sum(&a));
      }
}

 

 

 

그리드형

댓글