OneDev

[C] 19.함수(function) 본문

Language/C

[C] 19.함수(function)

one_dev 2022. 8. 23. 18:14

▣ 목차


1. 함수란?

2. main 함수

3. 함수 정의하기

4. 함수의 인자(argument) / 매개변수(parameter)

5. 함수 사용하기

6. 포인터로 받는 인자

7. 함수의 원형


1. 함수란?

프로그래밍에서 말하는 함수(function)란 하나의 특별한 목적을 수행하기 위해 설계된 코드들의 집합 으로 정의할 수 있다.

C언어에서 함수는 크게 ①표준 함수②사용자 정의 함수로 구분된다.

함수의 사용목적은 여러가지가 있겠지만, 가장 주된 목적은 반복작업에 있다.

자주 사용될만한 기능을 가진 코드를 함수로 정의해두면, 불필요한 반복노동을 피할 수 있고 소스코드의 가독성까지 좋아진다.

 

 

2. main 함수

C프로그램을 실행할 때 컴퓨터는 main 함수부터 찾는다.

즉, 컴퓨터는 프로그램을 실행할 때  프로그램의 main 함수를 호출하는것 부터 시작한다는 것이다.

만약 프로그램에 main 함수가 없다면 컴퓨터는 프로그램의 어디서부터 실행할지 모르게되어 오류가 나게 된다.

 

보통 main 함수는 다음과 같이 정의한다

int main()

이를 딱 봤을 때 우리는 "이 함수는 int 형을 리턴하고 이름은 main 이구나! " 정도를 알 수 있다.

그렇다면 이 main 함수가 리턴하는 값은 누가받을까?

main 함수는 프로그램의 시작점이기도 하지만, 맨 마지막에 종료되는 함수도 main함수 이기 때문에 리턴값을 받을 수 있는 함수가 없어 보이기도 한다.

사실 main 함수도 리턴값을 받아주는 곳이 있다.

main함수가 리턴하는 데이터는 운영체제에서 받아들이게 된다! (윈도우나 리눅스 등등)

 

보통 main 함수가 정상적으로 종료되면 0을 리턴하고, 비정상적으로 종료되면 1을 리턴한다고 규정되어있다.

 

아무튼 중요한 것은 "main 도 함수다 " 라는 것이다.

 

 

3. 함수 정의하기

함수의 기본적인 형태는 다음과 같다

(1) 함수 정의

int print_hello() {
    ...
    return 0;
}

위와 같이 int print_hello()   라고 써있는 부분을 함수의 "정의" 부분이라 부른다.

함수의 정의 부분에서 우리는 다음의 것들을 알 수 있다.

 

① 반환자료형 (위 예시의 경우 int)

함수 정의 부분에 쓰인 자료형은 이 함수가 어떤 자료형의 데이터를 반환하는지 나타낸다.

위 예시의 경우 print_hello() 함수가 반환하는 데이터의 자료형은 int형이 된다는 것이다.

 

또 위 예시에서 return 0; 라고 써져 있는 부분을 볼 수 있는데 이는 이 함수가 0을 반환한다는 의미이다.

즉, 위와 같은 함수를 사용하게 된다면 언제나 0이 출력된다.

이 때, 함수의 반환형이 int 이므로 0 은 int 의 형태로 저장되어 나가게 된다.

 

② 함수의 이름

많은 분들이 짐작하시겠지만, 위 함수의 이름은 print_hello 이다.

끝에 붙는 소괄호 () 는 함수의 이름에 포함되는 것은 아니고, "지금 적은 것이 함수다" 라고 알려주는 역할을 한다.

만일 소괄호 () 를 붙이지 않고 작성한다면

int print_hello

단순히 끝에 ; 를 붙이지 않았구나 라고 해석되어 오류를 출력하게 되니 유의하자.

함수의 이름은 궁극적으로는 짓는 사람 마음대로 지을 수 있지만, 흔히 "좋은" 함수의 이름이라 하면 그 함수가 어떤 기능을 하는지 잘 설명할 수 있는 이름을 말한다.

함수의  이름을 xdfoshjioar 와 같이 해놓으면 작성자 본인 외에 다른사람들은 전혀 이 함수가 어떤 일을 하는 친군지 알 수 없고, 작성자 본인도 시간이 흘렀을 때 까먹을 가능성이 높다.

또한 함수의 이름을 짓는데는 몇 가지 조건이 있는데, 이는 변수의 작명과 동일하다.(기억이 안난다면 변수 이름짓기 규칙을 찾아보자).

 

4. 함수의 인자(argument) / 매개변수(parameter)

아래의 예시를 통해 함수의 인자에 대해 알아보자.

 

함수는 반복적인 작업을 효율적으로 수행할 수 있게해주는 마법상자같은 것이라고 볼 수 있다.

예를 들어 우리가 어떤 두 정수를 입력으로 받고 그 수들의 합을 출력해야 하는 상황인데, 입력이 많아질수록 손으로 일일이 코딩하기는 쉽지 않아질 것이다.

이럴 때 어떤 두 수의 합을 구하는 함수를 만들어 놓고 입력값만 넣어준다면 우리는 훨씬 간편하게 작업을 수행할 수 있을 것이다.

두 수의 합을 구하는 기능을 가진 함수를 만들어보자.

#include<stdio.h>

int add(int x, int y) {    // 함수 이름을 add 라 하였다
	return x + y;    		// 이 함수는 입력한 두 수의 합을 반환한다!

}

int main() {
	int a, b;
	scanf("%d %d", &a, &b);     // 두 수 입력
	printf("%d", add(a, b));    // 두 수의 합 출력

	return 0;
}

두 수의 합을 구하는 함수 add 안에 int x, int y 라고 써있는 것을 볼 수 있을 것이다.

int add(int x, int y)

이는 "나를 호출하는 코드로 부터 어떤 값을 x, y 라는 int 형 변수에 인자로 받아들이겠다!" 라는 의미이다.

(인자(argument) 를 매개변수(parameter) 라고도 한다 / 수학에서의 매개변수와 살짝 의미가 다르니 참고)

이제 이 "인자" 라는 것이 무엇인지 알아보자.

위의 예시코드에서 우리는 크게 add 와 main 두 가지 함수를 볼 수 있는데, 각각은 별개의 함수이기 때문에

add 는 main 함수내의 정의된 변수를 사용할 수 없고, 마찬가지로 main 함수는 add 함수 내에서 사용된 변수들에 대해 알 길이 없다.

이런 상황속에서, main 함수 안에서 add 함수를 사용하고자 할 때 더해지는 두 수를 add 함수에 전달할 수 있을지에 대한 의문이 든다.

바로 인자(argument/ 매개변수 paramete라고도 부른다) 를 이용하면 이러한 일들이 가능해진다.

인자는 어떤 함수 내에 선언되어있는 변수로 , 함수가 호출될 때 전달되는 값을 의미하며 함수를 정의할 때 소괄호 안에 나타내게 된다.

 

※ 방금 뭉뚱그려서 설명했지만, 사실 인자(argument) 와 매개변수(parameter) 는 같은 것이 아니며 약간의 차이가 있다.

그 둘의 차이점은 쓰임새에 있는데,

  • 인자(argument) : 실제로 함수가 호출할 때 전달하는 변수값
  • 매개변수(parameter)  : 함수를 정의할 때 사용되는 변수( 또는 전달된 인자를 받아들이는 변수)

정도로 이해하면 될 듯 하다.

int add(int x, int y){ // a 와 b 는 매개변수(parameter)
....
....
int main(){
....
....
    add(2,3);     // 2 와 3 은 인자(argument) 
....
}

 

 

5. 함수 사용하기

이제 함수들을 사용하는 방법에 대해 알아보자.

 

(1) 라이브러리 함수 

라이브러리 함수는 미리 만들어져 컴파일러에서 제공하는 함수들이다.

예를 들면 printf, scanf 등은 stdio.h 헤더에서 제공하는 라이브러리 함수의 일부이다.

라이브러리 함수의 경우 헤더를 include 하면 사용 가능하다.

 

(2) 사용자 정의 함수

① 함수 정의하기

 먼저 사용할 함수를 정의한다. 

② 함수 원형 선언

 사용자 정의 함수가 main 함수 보다 위에 적혀있다면 상관없지만, 만약 main 함수 이후에 어떤 함수가 정의되어 있다면, 

main 함수 위에 그 함수의 원형을 제공해주어야 사용이 가능하다.

함수의 원형을 제공해주는 방법은 다음과 같다.

(반환자료형) (함수명) (매개변수 목록); // 함수 정의와 다르게 세미콜론 ; 을 붙여줘야 함에 유의

이 때, 함수의 매개변수의 이름은 필수가 아니고, 자료형만 필수적으로 적어주면 된다.

(예)

#include <stdio.h>

int function_a(int, int);  // function_a 라는 사용자 정의 함수의 원형 제공

int main() {
...
...
    function_a(num1, num2);  // 사용 가능!
...
...

 

 

 

6. 포인터로 받는 인자

어떤 함수가 특정한 타입의 변수/배열의 값을 바꾸려면 함수의 인자로는 반드시 그 타입을 가리키는 포인트를 이용해야 한다

예시로 두 변수의 값을 교환하는 함수를 생각해보자

두 변수의 값을 교환하는 함수 swap() 을 구현하여 main 함수해서 호출하는 코드이다.

두 변수의 값을 교환하는 과정 자체는 하자가 없어보이나, 실행해보면

두 변수의 값이 전혀 바뀌지 않았음을 볼 수 있다

왜 두 변수의 값이 바뀌지 않았는지에 대해 그림으로 간략히 나타내보았다.

사실 함수 swap 함수 내부의 변수 a,b 와, main 함수에서 선언한 변수 i,j 가 서로 무관한 변수들임을 생각하면 오히려 i, j 의 값이 바뀌는 것이 더 이상하게 느껴진다.

본래 목표했던 것처럼 두 변수의 값을 바꾸기 위해서는 포인터를 이용하면 된다

 

성공!

a와 b에 i와 j의 주소값을 전달하여 a 는 i 를, b는 j 를 가리키게 만들고, 이에 따라 swap 함수 내부에서는 a와 b 가 가리키는 두 변수의 값을 교환하여 성공적으로 i 와 j 의 값이 바뀌게 된 것이다!

 

7. 함수의 원형 제공

main 함수보다 뒤에(아래에) 정의되어 있는 함수의 경우, main함수에서 해당 함수를 호출할 때 경고 또는 오류가 발생하게 된다. 이러한 경우에 함수의 원형을 제공함으로써 문제를 해결할 수 있는데, 이는 소스코드 상단 (main함수 보다 위에)에 

반환형 함수이름 (매개변수의 데이터타입) 을 적어주는 것이다.

어떻게 보면 함수의 원형을 제공해주는 것은 함수의 정의 부분을 한 번 더 써준 것인데, 이는 컴파일러에게 "소스코드에 이러이러한 함수가 정의되어 있으니 잘 살펴봐!" 라고 알려주는 것이라 볼 수 있다.

(함수의 정의와 다르게 문장 끝에 세미콜론을 붙여줘야 함을 잊지말자)

#include <stdio.h>
/* 함수의 원형 */
int function_1(int, int); // 끝에 세미콜론(;)을 붙여줘야 된다
void function_2(int*);

int main(){
    ...
    ...
    ...
    return 0;
}

int function_1(int a, int b){
    ...
    ...
    return 0;
 }
 
 void function_2(int *a){
    ...
    ...
}

사실 모든 함수를 main함수 보다 위에 정의하면 원형제공을 안해도 상관없지만, 대다수의 프로그래머들이 함수를 main 함수의 뒤에 정의하고 원형을 앞에 추가하는 것을 선호한다니 참고하면 좋겠다.

 

Comments