함수

함수특정 동작을 수행하는 일정 코드 부분입니다. 반환하는 값의 자료형 함수명 (인자)의 형식으로 선언하며(반환 값의 자료형의 void는 아무 값도 반환하지 않는다는 뜻입니다), 어떤 함수를 호출하기 위해서는 그 함수를 호출하는 부분 위에 그 함수가 정의되어 있어야 합니다. 반환하는 값의 자료형이 int라면 생략할 수 있으며. 인자도 없다면 괄호 안을 생략할 수 있습니다. 반환하는 값의 자료형 앞에 inline을 붙여주면 함수를 호출하는 대신 호출한 곳의 내용이 함수의 내용으로 치환됩니다. 이 과정은 컴파일러에 의해 수행되며, 경우에 따라서는 프로그램의 수행 속도를 빠르게 해 줄 수도 있습니다. 하지만 그렇지 않은 경우도 있으니 유의해서 사용하십시오,

#include <stdio.h>
void func() {
      printf("퉤에에엣\n");
}
int main() {
      for(int i = 0; i < 4; ++i)
            func();
      return 0;
}

퉤에에엣

퉤에에엣

퉤에에엣

퉤에에엣

서로 다른 두 함수는 변수를 따로 사용합니다. 예를 들어, main 함수에서 i를 선언했어도, asdf 함수에서는 그 i를 사용하지 못합니다. 만일 asdf 함수에서 새로 i를 선언한다면, 그것은 maini와는 다른 i입니다.

#include <stdio.h>
void func() {
      //printf("%d\n", i);    i가 선언되지 않았습니다!
      for(int i = 0; i < 2; ++i)
            printf("퉤에에엣 ");
      printf("\b\n");
}
int main() {
      for(int i = 0; i < 4; ++i)
            func();
      return 0;
}

퉤에에엣 퉤에에엣
퉤에에엣 퉤에에엣
퉤에에엣 퉤에에엣
퉤에에엣 퉤에에엣

반환하는 값의 자료형 함수명 (인자)의 형식으로 함수 원형을 적어주면 함수의 정의부가 호출하는 곳보다 아래에 있어도 컴파일이 가능합니다. 단, 함수 원형은 함수 호출보다 위에 있어야 합니다.

#include <stdio.h>
void func();
int main() {
      for(int i = 0; i < 4; ++i)
            func();
      return 0;
}
void func() {
      printf("퉤에에엣\n");
}

퉤에에엣

퉤에에엣

퉤에에엣

퉤에에엣

인자는 변수를 선언하듯 자료형 이름으로 써줍니다. 자료형이 같아도 모든 인자 이름 앞에 자료형을 적어주어야 합니다.

#include <stdio.h>
void print(int a) {		//정수 인자를 1개 받으며, 그 수는 print 함수의
      printf("a: %d\n", a);	//a 라는 int 변수 속에 들어가 있습니다.
}
int main() {
      for(int i = 0; i < 4; ++i)
            print(i);
      return 0;
}

a: 0
a: 1
a: 2
a: 3

print 함수의 amain 함수의 i와 완벽히 동떨어진 존재입니다. 값을 공유하고 싶다면 변수를 함수 밖에 선언하거나(전역 변수), 포인터를 넘겨줍니다★. 다만 캐시 적중률이나 후술할 안전성 등의 이유 때문에 전역 변수 사용은 별로 권장되지 않습니다.

#include <stdio.h>
void func(int a) {
      ++a;
}
int main() {
      int k = 4;
      func(k);
      printf("k: %d\n", k);
      return 0;
}

k: 4

정 수정하고 싶다면 포인터를 넘겨줍니다★.

#include <stdio.h>
void func(int* a) {
      ++(*a);
}
int main() {
      int k = 4;
      func(&k);
      printf("k: %d\n", k);
      return 0;
}

k: 5

#include <stdio.h>
void func() {
      int a = 0;
      static int b = 0;
      ++a;
      ++b;
      printf("a: %d, b: %d\n", a, b);
}
int main() {
      for(int i = 0; i < 3; ++i)
            func();
      return 0;
}

a: 1, b: 1
a: 1, b: 2
a: 1, b: 3

함수는 호출될 때마다 변수를 초기화합니다. 하지만 자료형 앞에 static을 붙히면 함수가 몇 번이고 호출되어도 변수가 초기화되지 않습니다.

#include <stdio.h>
int addthree(int a) {
      return a + 3;
}
int main() {
      for(int i = 0; i < 4; ++i)
            printf("addthree(%d): %d\n", i, addthree(i));
      return 0;
}

addthree(0): 3
addthree(1): 4
addthree(2): 5
addthree(3): 6

C에서의 함수는 반환값이 있다면 인자와 반환값의 대응관계라 할 수 있습니다. 함수가 반환하는 값은 return에 적어주며, 함수는 return을 만나면 그 자리에서 즉시 값을 반환하고 종료됩니다. 눈치가 빠른 사람이라면 우리가 지금까지 썼던 main도 사실 함수이며, 다른 함수와의 차이점은 프로그램의 시작과 함께 가장 먼저 시작된다는 것뿐이라는 것을 알았을지도 모릅니다.

C에서의 함수는 수학에서의 함수와 약간 의미가 다를 수 있습니다. 우선, 값을 반환하지 않는 함수가 존재합니다. 또, 수학에서의 함수는 오로지 인자와 결과값의 대응 관계로, 인자가 같다면 결과값도 언제나 같지만, C에서는 static 변수나 전역 변수의 존재로 인해 인자가 같을 때 결과값도 언제나 같다는 보장을 할 수 없습니다. 이 때, 인자가 같을 때 결과값이 언제나 같다는 성질을 참조 투명성이라 하며, 이러한 성질을 갖는 함수를 순수한 함수라 합니다. 또, 함수의 실행이 외부에 영향을 미치는 상황(전역 변수 수정 등)을 사이드 이펙트라고 부릅니다.

함수가 사이드 이펙트를 일으키지 않는다면, 이 함수들은 동시 처리(병렬 처리)가 가능합니다. 여기에서는 다루지 않았지만, 멀티 스레딩을 통해 서로 상관관계가 없는 함수들을 동시에 실행시킬 수 있습니다.

다른 함수로 goto를 사용해 점프하는 것은 불가능합니다. 아까 보여주었던, 레이블을 변수처럼 다루는 것을 사용해도, 프로그램은 문제를 일으키며 종료될 것입니다. 굳이 다른 함수 중간으로 건너뛸 일이 있다면, setjmp/longjmp를 사용하세요.