프논사 카테고리의 맨 첫 시간때 main이라는 함수를 본 적이 있습니다.

이 때는 프로그래밍 언어에 대한 지식이 아예 없는 분들을 고려해 가장 기본이 되는 main함수를 최대한 상세하게 설명했는데, 이번에는 main이 아닌 일반적인 함수에 대해 작성하는 글이 되겠습니다.

 

목차

     


    0. 함수의 기본

    0.0. 함수의 작동원리

    함수프로그램에서 자주 사용되는 코드 블록을 따로 만들어두고 필요할 때마다 불러서 사용하는 기능입니다.

     

    이렇게 함수를 사용할 때의 장점은 아래와 같습니다.

    - 코드가 중복되지 않고 간결해집니다.

    - 한 번 작성해둔 코드를 여러 번 사용하므로 코드의 재사용성이 증가됩니다.

    - 기능별로 함수를 작성해서 사용하므로 프로그램의 모듈화가 보다 쉬워집니다.

    - 프로그램의 기능을 함수 단위로 나누어 묶어 두었기 때문에 코드를 수정하기 쉬워집니다.

     

    아래 그림을 보시면 함수를 한번 짜두면 다음에 그 함수의 기능을 사용할 때는 이미 만들어 놓은 함수만 사용하면 되기 때문에 자주 사용하는 기능일 경우 효용성이 극대화됩니다.

     

     

    C에 존재하는 함수를 세 가지로 나누면 진입점 함수(main함수), 라이브러리 함수(이미 정해진 함수), 사용자 정의 함수로 나뉩니다.

    세 함수 종류의 특징을 간결하게 요약하면 아래와 같습니다.

     

     

    함수를 정의하는 기본적인 형식은 임의의 값을 입력받아서 특정한 출력값을 출력하는 형태입니다.

    여기서 입력값과 출력값은 없을 수도 있습니다.

    int main( ) { } 을 이해할 때 했던 것과 동일합니다.

     

     

    함수를 구성하는 요소를 몇 가지로 나누면 위 예제에 나와있는 것처럼 리턴형, 함수명, 데이터형, 매개변수명, 실행 문장으로 나눌 수 있습니다.

     

    함수의 리턴형함수가 처리 결과로 리턴하는 값의 데이터형입니다.

    함수의 리턴형이 void형이면 함수의 리턴값이 없다는 의미가 되고,

    리턴값을 생략하면 int형으로 간주합니다. (되도록 생략하지 않습니다)

    또한 함수는 반드시 하나의 값만 리턴할 수 있습니다.

     

    함수의 이름은 말 그대로 함수의 이름입니다.

    함수의 이름을 지을 때는 되도록이면 어떤 일을 하는 함수인지 명확하게 알 수 있는 이름을 선택하는 것이 좋습니다.

    그리고 이름이 같은 함수를 여러 번 정의할 수 없습니다.

     

    함수의 데이터형의 경우 아래에서 나올 매개변수의 데이터형이 들어갑니다.

    int, double, char 등이 들어갈 수 있습니다.

     

     

    함수의 매개변수함수의 기능을 수행하기 위해서 필요한 값을 넘겨주기 위한 변수입니다.

    매개변수라고도 부르고 인자라고도 부르고 파라미터라고도 부릅니다.

    매개변수에는 개수에 제한이 없고, 매개변수를 갖지 않을 때에는 void를 적어줍니다.

     

    함수는 헤더와 바디로 구성되는데, 함수의 실행 문장은 바로 바디 부분입니다.

    헤더는 리턴형, 이름, 매개변수를 적는 부분이고 바디는 { } 안에 함수가 처리할 문장을 나열한 부분입니다.

     

    위에 잠깐 언급했지만, 함수는 리턴값과 매개변수가 없을 수도 있습니다.

    그럴 경우 데이터형, 혹은 매개변수에 void를 작성합니다.

    void를 리턴값으로 받는 경우 따로 return문을 쓰지 않아도 함수의 끝을 만나면 자동으로 종료합니다.

    혹은 문장의 중간에 return을 넣으면 그 위치에서 종료됩니다. (뒷 문장은 의미가 없어집니다!)

     

    기본적으로는 매개변수(인자)와 리턴값을 모두 갖기 떄문에 그 케이스를 예로 들면 아래 그림과 같이 함수가 동작하게 됩니다.

     

    아래는 GetFactorial과 GetSum을 이용해 팩토리얼과 합계를 구하는 코드입니다.

    지금까지 배웠던 반복문 for, 기본적으로 정의되어있는 함수인 printf() 등을 복습하면서 공부하시면 좋을 것 같습니다!

    /* Ex06_03.c */
    #include <stdio.h>
    
    int GetFactorial(int num)
    {
    	int i;
    	int fact = 1;
    	for (i = 1; i <= num; i++)
    		fact *= i;
    	return fact;
    }
    
    int GetSum(int num)
    {
    	int i;
    	int sum = 0;
    	for (i = 1; i <= num; i++)
    		sum += i;
    	return sum;
    }
    
    int main(void)
    {
    	int result1, result2;
    
    	result1 = GetFactorial(10);
    	printf("10 팩토리얼= %d\n", result1);
    
    	result2 = GetSum(10);
    	printf("1~10의합계= %d\n", result2);
    
    	return 0;
    }

     

    추가로 설명드리자면

    result1 = GetFactorial(10);

    이 부분에서 GetFactorial(10)의 return값인 fact가 3628800이고, 이 return값이 그대로 result1에 할당되고 그 값이 printf를 통해 출력되면서 동작하게 됩니다.

     

     

    0.1. 함수 호출 시 주의사항

    단, 함수를 호출할 때 몇 가지 주의해야 할 점이 있습니다.

     

    먼저 함수의 인자로 수식을 사용하게 되면 먼저 그 수식의 값을 평가하고 나서 그 값을 함수의 인자로 사용합니다.

     

    또한 함수를 호출할 때 넘겨주는 인자의 개수는 함수 정의에 있는 매개변수의 개수와 일치해야 합니다.

    일치하지 않는 경우 컴파일타임(컴파일을 하는 시점)에 에러가 발생하게 됩니다.

     

    함수를 호출할 때 인자는 반드시 넘겨줘야 하지만 함수의 리턴값은 받아올 수도 있고 받아오지 않을 수도 있습니다.

    아래는 void PrintSumAndAverage(int a, int b); 함수가 있다고 가정할 때의 예시입니다.

    void를 결과값으로 가지는 함수는 다른 수식에서 사용할 수 없습니다.

     

    그리고 잘못된 이름으로 함수를 호출해서는 안됩니다.

    컴퓨터는 기본적으로 대문자와 소문자를 아예 다른 문자로 인식하기 때문에 확실하게 지켜줘야 합니다.

     

    마지막으로 함수를 호출할 때는 반드시 함수 호출보다 앞 쪽에 함수를 정의해야 합니다.

    컴퓨터는 C언어를 위에서부터 쭉 내려오면서 읽기 때문에 함수를 사용했는데 함수가 정의가 되어있지 않으면 당연히 정상적으로 작동하지 않습니다. (컴파일러에 따라 실행은 되지만 경고가 나타날 수 있습니다.)

     

    0.2. 함수의 선언

    함수 사용시 주의사항에서 맨 마지막에 함수 호출시 함수 호출보다 앞쪽에 함수가 정의가 되어야 한다고 했는데,

    만약 위에서 함수를 선언한다면 아래에서 정의를 할 수 있습니다.

    이 말은 함수의 선언함수가 정의된 위치에 관계 없이 함수를 호출할 수 있게 하는 것입니다.

    함수의 선언을 함수의 원형(prototype)이라고도 합니다.

     

    함수를 선언할 때는 함수의 헤더부분만 작성합니다.

    매개변수의 이름은 생략할 수 있고, 함수 선언문의 끝에 세미콜론을 꼭 써주어야 합니다.

     

    지금까지 배운 내용을 모두 정리하면 함수는 정의, 호출, 선언이 가능합니다.

    정의는 그 함수가 어떤 내용을 받아서 어떻게 처리하는 지를 나타내는 것이고,

    호출은 만들어놓은 함수를 이용하는 것이고,

    선언은 함수의 내용을 알려주지는 않지만 함수 호출에 필요한 리턴형, 함수명, 매개변수 정보를 미리 알려주는 역할을 합니다.


    1. 지역변수와 전역변수

    함수를 배웠다면 빼놓을 수 없는 것이 있습니다.

    바로 지역변수전역변수 개념입니다.

    변수는 기본적으로는 만들어진 곳에 따라서 목숨이 왔다갔다(?) 합니다.

    우리가 일반적으로 변수를 만들 때는 함수 내에서 만드는데, 간혹 밖에다가 변수를 만드는 경우가 있습니다. 이를 전역변수라고 부릅니다. 어디서든 사용할 수 있는 변수라는 뜻이죠.

    (main함수도 결국 함수기 때문에 main함수 내에 만드는 변수는 전역변수가 아닙니다!!

    하지만 함수 내에서 변수를 만들면 그 변수는 함수가 끝나면 더 이상 필요가 없어지기 때문에 사라져버립니다. 그렇기에 그 지역에서만 사용할 수 있다는 의미에서 지역변수라는 이름이 붙습니다.

     

    1.0. 지역변수

    지역변수함수 안에서 만들어지고, 함수 안에서만 사용되는 변수입니다.

    함수가 리턴될 때 자동으로 사라집니다.

    자기 지역이 아니면 바로 사라져버리는 것이죠.

     

    만약 다른 함수에서 같은 이름의 변수를 선언한다고 해도 오류가 발생하지 않습니다. 어짜피 자기 지역에서만 존재하는 변수들이기 때문에 서로가 꼬일 일이 없는거죠!

     

    또한, 함수의 매개변수도 지역변수입니다.

    매개변수로 num이라는 int형 변수를 받았다면, num은 함수가 종료되면 메모리에서 없어집니다.

     

    그리고 지역변수 역시 따로 초기화하지 않으면 쓰레기값을 가지게 됩니다!

    Visual Studio에서는 오류를 뿜어냅니다.

     

    1.1. 전역변수

    전역변수프로그램 전체에서 사용될 수 있는 변수입니다.

    프로그램이 시작될 때 한 번만 생성되고, 프로그램이 수행되는 동안 여러 함수에서 사용되다가 프로그램이 종료될 때 메모리에서 없어집니다.

     

    전역변수의 선언문 다음에 정의된 함수에서만 전역변수를 사용할 수 있습니다.

    당연히 선언도 되지 않았는데 막 사용할 수는 없겠죠?

    대신 전역변수는 따로 초기화하지 않으면 자동으로 0으로 초기화됩니다.

     

    1.2. 함수의 영역 규칙

    만약 블록의 범위가 달라지면 같은 이름의 변수를 여러 번 선언할 수 있습니다.

    for문, if문, while문 안에서 변수를 만들면 그 반복문 혹은 제어문이 끝나는 순간 안에서 만든 변수는 모두 사라지게 됩니다. 지역이 사라지니 지역 안에서 만든 변수도 사라지게 되는 것이죠. 그래서 또 쓸 수 있습니다!

     

    이렇게 해도 전역변수와 지역변수간의 이름이 간혹 겹치게 될 수도 있습니다.

    이런 경우를 대비하여 전역변수임을 나타낼 수 있는 표시를 이름에 붙이면 보다 편히라게 구분할 수 있습니다.

     


    2. 함수의 인자 전달 방법

    함수에 인자를 전달할 때는 값을 전달할 수도 있고, 포인터를 전달할 수도 있습니다.

    아직 포인터에 대한 내용을 다루지 않았기 때문에 이 부분은 최대한 간략하게 설명하도록 하겠습니다.

     

    먼저 값을 전달하는 방법의 경우 함수를 호출할 때 넘겨주는 인자의 값을 함수 정의에 있는 매개변수로 복사★해서 전달합니다.

    복사했기 때문에 함수 안에서 매개변수의 값을 지지고 볶고 변경을 하더라도 함수를 호출한 곳에 있는 원래 인자의 값은 바뀌지 않습니다.

    아래는 값을 바꿔주는 Swap 함수를 만들었습니다.

    하지만 두 변수의 값이 바뀌지 않습니다. 값을 복사한 것을 죽어라 바꿔도 원래 변수는 변하지 않으니까요!

    #include <stdio.h>
    
    void Swap(int x, int y);
    
    int main(void)
    {
    	int a = 10;
    	int b = 20;
    
    	printf("Swap 젂의a = %d, b = %d\n", a, b);
    
    	Swap(a, b);
    
    	printf("Swap 후의a = %d, b = %d\n", a, b);
    
    	return 0;
    }
    
    void Swap(int x, int y)
    {
    	int temp;
    	temp = x;
    	x = y;
    	y = temp;
    }

     

    그렇다면 어떻게 원래 값을 변화시킬 수 있을까요?

    값 대신 주소(포인터)를 전달하는 방식을 통해 원래 값을 전달할 수 있습니다.

    포인터 변수를 매개변수로 받아서, 함수를 사용할 때 주소값을 넣어주면 함수가 그 주소에 직접 접근해서 값을 바꿔주기 때문에 원래의 값이 변화하게 됩니다.

    #include <stdio.h>
    
    void Swap(int *x, int *y);
    
    int main(void)
    {
    	int a = 10;
    	int b = 20;
    
    	printf("Swap 전 a = %d, b = %d\n", a, b);
    
    	Swap(&a, &b);
    
    	printf("Swap 후 a = %d, b = %d\n", a, b);
    
    	return 0;
    }
    
    void Swap(int *x, int *y)
    {
    	int temp;
    	temp = *x;
    	*x = *y;
    	*y = temp;
    }

     

    이 부분은 이해하지 못하는게 정상입니다. 아직 포인터와 주소값에 대해서 배우지 않았으니까요!

    이 궁금증은 이후 포인터 파트에서 해결하실 수 있습니다. 이미 이 포스팅에서는 너무 많은 양을 다루고 있어서 포인터까지 다루기에는 무리가 있습니다 ㅎㅎㅎㅎ


    줄이고 줄인다고 노력했지만 함수 파트는 쉽지 않았습니다. 아예 게시물을 2개로 나눌까도 생각했는데, 쓰다보니 결국 한번에 쓰게 되었네요.

    그래도 꽤 만족스럽습니다. 만약 정상적으로 모든 글을 읽으셨다면, 분명 포인터에 대한 궁금증이 생기실 테니까요.

    이 부분은 다다음 포인터를 주제로 한 포스팅에서 자세하게 설명할 예정입니다! (다음 게시물은 아쉽게도 포인터가 아닌 배열과 문자열입니다.. ㅋㅋㅋㅋ)

     

    그렇다면 다음 포스팅에서 뵙겠습니다!!

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기