[C언어] 함수
오늘은 함수에 대해 배워보자.
사실 C언어를 배우는 내내 우린 계속 함수를 써왔다.
함수이름(매개변수)
{
몸체
}
이렇게 함수는 함수명과 몸체 등으로 구성 되어있다.
프로그램이 수행하고자 하는 기능이 있지?
그 기능에 대한 명령문들의 집합이 함수인 것이다.
프로그래머들은 프로그램에서 자주 사용 되는 코드블록은 한 번 만들고 그걸 재사용하면 어떨까 생각했다.
아래의 예시를 보자.
위와같이 우리가 자주 쓰던 printf다. () 안에 원하는 문자만 입력하면 그대로 출력이 된다.
난 입력만 했을 뿐 , 화면에 글자를 나타내는 기능 자체는 내가 만든 게 아니다. 그건 printf가 한 것이다.
그리고 printf의 실제 코드는 아래와 같다.
우리가 간편히 작성했던 printf 코드 한 줄은 이렇게 복잡했다. 그 내부에도 함수호출이 있으니 더 풀어놓으면 더 복잡할 것이다.
이렇게 자주쓰이는 복잡한 코드를 프로그래머들이 printf 한줄로 쓸 수 있도록 정의해 놓은 곳이 바로 stdio.h 파일이다. 우리가 맨날 봐왔던 바로 그 헤더파일이다. #include <stdio.h>로, 이 stdio.h 파일을 포함시킴으로써 그 안에 정의 되어있는 (printf 등등) 함수들을 이용해 프로그램을 만들겠다는 의미이다.
내가 직접 만든 코드(기능)은 아니지만, 이미 정의된 많은 기능을 편리하게 이용할 수 있게 되는 것이다.
이렇게 printf()나 rand() 등 다른 사람이 만든 함수와
내가 직접만드는 함수가 있다.
이번에 배워볼 것이 바로 함수 직접 만들기 !
감성글을 작성해보았다.
반복되는 부분은 함수로 만들 수 있겠다.
void는 리턴하지 않는 함수라는 뜻이다. () 안에는 변수(인자/variable)가 들어가는데 필요가 없으니 빈칸으로 남겨두었다. 그런데 중간에 다른 스타일의 라인을 넣고 싶어졌다. 스타일을 한 가지 더 추가해보자. 이럴 때 변수가 필요하다.
type 값을 정수로 받을 것이기 때문에 int로 정의하였다.
그런데 만약 함수마다 return 값을 주고싶다면?
처음에 void는 리턴값이 없는 함수라고 했다. 정수형 리턴값을 받기위해 이를 int로 바꿔보자
임의로 정수 리턴값을 매겨주었다. 함수의 자료형과 return 값은 같아야한다.
이제 제대로 출력이 되는지 확인해 보자. msg라는 정수형 변수를 만들어 printf로 line함수를 출력해보았다.
잘 출력이 되었다. 리턴값은 각 함수가 남기는 유서라고 생각하면 되겠다.
만약 한 함수에 리턴값이 여러개라면?
모든 함수는 첫번째 return을 만나자 마자 함수가 종료되어 버린다.
void 함수에 return을 넣고싶다면 그냥 값없이 return; 만 작성해주면 된다.
그렇다면 printf함수도 return값을 가질까? 확인해보자
보이다시피 작성된 문자의 개수가 리턴값으로 표시되는 걸 알 수 있다
+++ 아래와 같이 %d의 대입값으로 정수형 함수를 대입할 수도 있다!
매개변수(Parameter) vs 전달인자(Argumet)
함수에서 이 둘은 같은 의미로 쓰이는 경우가 있는데 명백히 다르다.
위에서 (int x, int y)는 실제값은 없고 형태만 나타낼 뿐이다. 이때 이 둘을 매개변수라고한다.
그리고 아래에서 add 함수를 호출하였을 때 5, 3 이라는 실제값이 있다. 이 변수를 전달인자라고한다.
add은 매개변수가 있는 함수이고, main은 매개변수가 없는 함수라고 할 수 있다.
그리고 여기서, int sum; 이 두개인 걸 볼 수 있는데 변수는 중복될 수 없다고 하지 않았는가?
이는 sum이 지역변수(local variable)이기 때문이다. 어떠한 함수 안에서 정의된 변수는 그 함수 내에서만 유효한 지역변수이다. 그렇기 때문에 함수가 종료되면 지역변수에도 메모리에서 없어진다. 그래서 여러 함수에 같은 이름의 변수가 등장할 수 있는 것!
그리고 함수 밖에서 선언되어 제한 없이 프로그램의 모든 곳에서 사용할 수 있는 것은 전역변수(global variable)이라고 한다.
함수정의와 함수선언
이제까지 함수를 만드는 방법을 배워보았다. 이는 함수의 정의에 해당된다.
함수선언은 아래 예시의 빨간 동그라미가 해당된다.
만약 변수를 하나하나 설정하는 게 귀찮으면
int add(int, int); 이런 식으로 int의 개수까지만 정해줘도 된다.
함수정의는 함수원형(prototype)에 세미콜론을 붙여서 선언하며, main 함수 위쪽에 선언을 하도록 한다.
함수를 선언하는 이유는 아래와 같다.
1. 함수 호출 확인
함수를 미리 선언하면 함수를 호출한 곳에서 올바른 형식으로 호출했는지 컴파일러가 검사 할 수 있다.
함수 선언으로 매개변수의 타입과 개수를 알 수 있기 때문이다.
2. 메모리 공간 확보
함수를 선언한 곳에서 미리 반환값형태를 파악하고 있다가 어디선가 함수가 호출될 경우, 반환값과 같은 형태의 저장공간을 확보한다. 반환형태가 int면 int 타입의 메모리공간을, double이면 double 타입의 메모리 공간을 호출한 곳에 준비한다.
재귀함수(Recursive function)
말 그대로 함수 내에서 자기 자신을 다시 호출해서 실행하는 함수이다.
이번엔 변수를 만들어, 좀 더 유의미한 함수를 만들어 보자
이번엔 이 함수가 어떤 숫자에 도달했을 때 종료 될 수 있도록 return을 이용해보자
팩토리얼
왜 갑자기 팩토리얼을 꺼냈느냐~~
우선 팩토리얼은 간단하게 n!로 쓰이며 1부터 n까지 모든 자연수를 곱하는 것을 의미한다.
n! = 1*2*3*...(n-1)*n
ex) 4! = 1*2*3*4 = 24
여기서 조금만 생각해보면
n! = n*(n-1)! 라는 걸 알수 있다.
그렇다 여기에도 자기자신의 정의에 자기자신이 등장하는 재귀함수의 꼴이 나타난다.
그럼 팩토리얼을 재귀함수로 재연해보자!
여기서 중요한 건 if( n == 1) return 1; 이 부분이다.
만약 return n * fac(n - 1)로만 함수를 끝냈다면 함수는 끝이 없을 뿐더러, 팩토리얼의 정의에도 부합하지 못한다.
n * (n -1) * (n -2) .... * 1 로 끝내기 위해 넣어준 것이다. 이해를 돕기위해 아래 그림을 보자
포인트 꼭 기억하자 ! n==1 return 1
배열을 매개변수로 받기
(배열은 다음게시물에서 다뤘으니 모르면 보고 다시 오시면 됩니다 ! )
바로 예시로 알아보자
다섯 과목의 성적이 담긴 배열 score가 있다.
이 배열을 매개변수로 받아, 전체평균을 구하는 함수를 만들어 보자.
여기서 포인트는~
Avr(int x[])와 같이 매개변수를 임의의 배열형태로 입력해주고,
실제 배열을 입력할 땐 배열의 이름만 적어주면 된다 ( Avr(score) )
앞서 우리는 C언어는 call by value 라고 배웠다. 이 경우는 어떻게 포인터 없이 달라진 값을 메인함수에 전달할 수 있었을까? 배열명자체가 배열의 첫번째 주소가 되기 때문.
이번엔 2차원 배열을 매개변수로 받아보자
마찬가지로 함수를 만들 때만 원하는 크기대로 초기화를 시켜주고, 메인 함수에서 적용할 때는 그냥 배열의 이름을 전달인자로 넣으면 된다~