'함수'는 특정한 기능을 수행하도록 설계된 독립적인 프로그램입니다. C 프로그램은 이런 함수들로 구성되고, 이 함수들이 정해진 실행 순서에 따라 실행됩니다. C 프로그램은 전체의 실행 내용을 몇개의 모듈(module)로 분류하고 각각의 모듈에 해당하는 실행 내용을 기술한 함수를 작성하여 실행 순서에따라 그 함수들을 차례로 호출 및 실행합니다. 이렇게 모듈 단위로 함수를 작성하면 '수정이 용이'해지고, '유지 관리가 쉬워'지며, '함수를 재사용함으로써 코드 중복을 최소화'합니다.
모듈(module) : 프로그램의 일부분을 의미하며, 하나의 함수나 여러 함수와 변수들의 집합입니다. 하나의 파일도 모듈이 될 수 있습니다. 이러한 모듈은 특정 기능을 가지고 나누어진 하나의 독립적인 프로그램 코드의 단위입니다.
'표준 함수'는 C에서 자체적으로 제공하는 함수이며, '표준 라이브러리'라는 이름으로 제공됩니다. 앞서 사용했던 printf() 함수는 '표준 입출력 함수'이며, 이 외에도 수학 연산 관련 기능이있는 '수학 함수' 등 수많은 표준 함수가 존재합니다.
표준 함수를 사용하려면 그 표준 함수의 양식에 해당하는 "함수 원형(prototype)'과 '실체'를 알아야합니다. C에서 함수원형은 '헤더 파일'에 정의되어있고, 실체는 '라이브러리 파일'에 수록되어있습니다. 예를 들어 printf() 함수의 원형은 int printf(const char *format, ...);
로, 'stdio.h'라는 헤더 파일에 선언되어있고, 실체는 '링킹(linking)' 과정에서 라이브러리 파일에서 읽어와서 프로그램에 연결시켜줍니다. 따라서 해당 함수를 사용하기위해서는 함수 원형이 선언되어있는 헤더 파일을 '#include'를 통해 전처리기가 처리할 수 있게끔 해주어야합니다.
printf()처럼 C 자체에서 사용하는 표준 함수 외에도 사용자가 필요에 따라서 코드의 중복을 막거나, 전체 길이가 길어지는 것을 막기위해 짧은 길이로 나누어 사용하기위해 '사용자 정의 함수'를 사용할 수 있습니다.
함수가 수행할 작업을 작성해놓은 코드를 '함수의 정의'라고 합니다.
반환_자료형 함수명(자료형 매개변수1, 자료형 매개변수2, ...) //함수 헤더
{ //함수 시작
함수 내용
} //함수 끝
void print_hello(int count){...}
함수가 수행할 명령문을 작성해줍니다.
int main(){ //int형을 반환하며 함수명은 'main'이고 매개변수가 없으므로 'void'를 작성해주거나, 이처럼 생략 가능합니다.
명령문;
...
명령문n;
return 0; //main 함수의 반환형이 int이므로 'return'을 사용하여 정수를 반환합니다.
}
위는 일반적인 'main' 함수의 형태입니다.
'return' 부분에서 '0'을 반환해주었는데, 반환값이 이처럼 0인 경우 시스템은 프로그램이 에러없이 정상 종료되었다고 판단합니다. 다른 함수와 달리 main 함수의 종료는 프로그램의 종료이며, main 함수의 반환값은 시스템으로 전달됩니다.
return : return문은 함수값을 반환할 때 사용합니다. 함수안에서 여러번 작성할 수도 있지만 한번이라도 return문이 실행되면 해당 함수는 종료됩니다. 반환값은 수식을 사용할 수도있으며, 함수의 반환형이 void라면
return;
으로 적어줍니다.
#include <stdio.h>
int sum(int x, int y){ //함수 정의
int s;
s = x+y;
return s;
}
int main(){
int a=1, b=10, c;
c = sum(a, b); //함수 호출
printf("%d", c);
return 0;
}
#include <stdio.h>
int sum(int x, int y); //함수 원형(prototype) 선언
int main(){
int a=1, b=10, c;
c = sum(a, b); //함수 호출
printf("%d", c);
return 0;
}
int sum(int x, int y){ //함수 정의
int s;
s = x+y;
return s;
}
위의 두가지 예시는 같은 동작을 하는 프로그램입니다. 하지만 위의 예시에는 따로 prototype을 선언하지않습니다.
C는 컴파일러가 위에서 아래로 내려오면서 코드를 읽는데, 위의 예시는 사용자 정의 함수가 main함수 위에 있기때문에 컴파일할 때 문제가 없습니다. 하지만 아래 예시는 사용자 정의 함수가 main함수 보다 아래에 있기 때문에 main 함수 안에서 사용자 정의 함수를 호출했을 때 컴파일러가 그 함수를 읽은 적이 없기 때문에 오류가 나게됩니다. 이는 함수도 변수처럼 사용 전에 미리 선언되어야 하기때문입니다. 따라서 사용자 정의 함수가 main 함수보다 아래에 있다면 아래 예시처럼 함수 원형(prototype)을 main 함수 전에 선언해주어야합니다.
지역 변수(local variable) : 선언된 블럭이나 함수 안에서만 사용 가능합니다.
전역 변수(global variable) : 함수 밖이나 외부 파일에서 선언되어 프로그램 전체에서 사용 가능합니다.
만약 같은 범위에서 전역 변수와 지역 변수의 이름이 같다면 지역 변수가 사용됩니다.
'기억 클래스(storage class)'는 변수를 기억공간의 특정 영역에 할당하는 방법입니다. C의 모든 변수는 자료형과 기억 클래스를 갖는데, 기억 클래스는 변수가 어디 사용될 것인지(사용 가능한 범위), 언제까지 존재할 것인지(life time)와 초기화 방법을 결정합니다.
형식은 기억_클래스_예약어 자료형 변수명;
으로 평소 변수를 선언하던 형식에서 앞쪽에 '기억 클래스 예약어'만 붙여주면됩니다.
'자동(auto) 변수'는 보통 함수 실행시 만들어지고 종료시 기억공간에서 제거됩니다. 예약어는 'auto'이며 생략 가능합니다. 사용가능한 범위는 해당 블럭이나 함수 안이고, 기억공간 중 '스택(stack)'에 할당됩니다. '지역(local) 변수'가 해당되며, 자동 변수는 항상 쓰레기값을 가지므로 반드시 초기화가 필요합니다.
쓰레기값(garbage value) : 의미없는 값을 의미합니다. 정적 변수같은 몇몇 경우를 제외하고 C에서는 변수 선언시 초기화를 따로 하지않으면 7212268(방금 쓰레기값을 확인해서 실제로 나온 값 입니다.) 등의 의미없는 아무런 값을 해당 변수에 할당합니다. 이는 특정 값을 할당하여 발생하는 시간 손실을 최소화하기위한 것 입니다.
'정적(static) 변수'는 기억공간을 프로그램 종료시까지 계속 유지하며, 예약어는 'static'입니다. 정적인 기억공간을 할당받는데, 이는 stack같은 임시 기억공간이 아닌 일반적인 기억공간을 의미합니다. 따라서 함수 종료나 블럭 밖에서도 값이 계속 유지되며, 모든 '전역(global) 변수'가 이에 포함됩니다. 정적 변수는 자동 변수와 달리 컴파일할 때 한번 기억공간을 할당하고 초기값(특별한 지정이 없으면 0)을 부여합니다.
정적 변수는 전역 변수의 특징을 가지면서 선언된 지역에서만 접근이 가능하며, 변수값은 프로그램 실행 중 계속 유지되고, 전역 변수와 달리 'extern'으로 다른 파일에서 참조 불가합니다.
#include <stdio.h>
static int g; //전역 변수 g
void increase();
int main(){
for(int i=0; i<5; i++){
increase();
}
}
void increase(){
static int s; //정적 변수 s
auto int a=0; //자동 변수 a
g++, s++, a++;
printf("static g : %d, static s : %d, auto a : %d\n", g, s, a);
}
static g : 1, static s : 1, auto a : 1
static g : 2, static s : 2, auto a : 1
static g : 3, static s : 3, auto a : 1
static g : 4, static s : 4, auto a : 1
static g : 5, static s : 5, auto a : 1
'외부 변수'는 함수 밖에서 선언되어 프로그램 종료시까지 값이 유지되며 초기값은 '0'입니다. 예약어는 'extern'을 사용하며, 외부 변수는 다른 파일에서 선언된 변수의 값을 참조할 수 있습니다. 정적 변수와 마찬가지로 기억공간은 일반 기억공간을 할당 받으며, 컴파일 시 초기화됩니다.
'레지스터(register) 변수'는 CPU 안의 레지스터에 자료를 저장할 때 사용합니다. 레지스터 변수를 사용하는 이유는 실행 속도 향상을 위해서인데, 레지스터의 속도가 기억장치보다 빠르기 때문입니다. 자동 변수와 동일한 속성을 가지며, 예약어는 'register'이고, 전역 변수로는 선언할 수 없습니다. 레지스터의 용량을 넘는 크기의 자료형으로는 사용할 수 없고, 기억공간에 저장되는 것이 아니므로 포인터로 활용할 수 없습니다.