IT Japan

제 5장 - 함수 / 기억부류 본문

IT/Programming

제 5장 - 함수 / 기억부류

swhwang 2016. 3. 22. 00:12
반응형

이제 이 강좌도 후반으로 접어 들었다. 그 동안 C의 기초적인 개념을 이 

해하기 쉽게 하고자 예제도 추가하고, 설명도 많이 하였다. 그러한 것들이 

효과적으로 전달되었는지는 모르지만 열번 찍어 안 넘어 가는 나무가 없다 

고 하였다. 여러분들이 해치워버린다는 마음가짐으로 달려들면 못 할 것도 

없다. 끈기로 끝까지 잘 따라와 주시기 바란다. 


이번 장에서는 C에서의 함수 제작법과 내장 함수들을 간략히 소개하고 변 

수의 기억범위에 대해서 설명하고자 한다. 



1. 내장 함수 



컴퓨터에서는 함수를 '주 프로그램으로부터 인수(Argument)를 전달받아 

일련의 작업을 수행한 후 생성된 결과를 주프로그램(main함수)으로 전달하 

는 하나의 단위 프로그램'으로 정의한다. 


이 함수에는 컴파일러의 내장 함수(라이브러리 함수)와 사용자가 직접 만 

들어 사용하는 사용자 함수의 2가지 종류가 있다. 함수는 상호간의 독립성 

을 유지함으로써 프로그램을 구조적으로 구현하는데 많은 공헌을 한다. 

(제 2장 참고) 우리가 지금까지 사용해온 printf(), scanf(), getch() 등 

등은 모두 컴파일러에서 제공되는 내장 함수이다. 


이 내장 함수의 종류는 수 백가지나 되는데, 이러한 함수들의 사용법은 

컴파일러와 함께 제공되는 'Reference guide'에 모두 나와 있다. 그러므로 

이 레퍼런스 가이드는 프로그래머의 필수품이라고 할 수 있다. 여러분들도 

C에서 지원되는 풍부하고 다양한 함수들을 이용하려면 반드시 레퍼런스 가 

이드를 장만하기를 권한다.(필수사항) 


C에서는 이러한 수백개의 내장 함수들을 용도별로 나누어 놓았다. 

먼저, 제2장으로 돌아가서 2번 항목을 자세히 읽고, 이 글로 다시 돌아와 

주기 바란다. .. 


2장에서 언급하였듯이 모든 함수들( main()함수 제외 => main()도 함수이 

다 )은 사용되기 전에 변수의 경우와 같이 프로그램 첫머리에 선언이 되어 

야 한다고 했다. 확장자가 .h인 화일들(헤더화일(Header file)이라고 함) 

이 바로 내장 함수들을 기능별로 구분하여 선언 해 놓은 화일인데 우리는 

프로그램 첫머리에 #include문을 사용함으로써 이 함수들을 선언한 화일 

을 프로그램에 포함시킬 수가 있는 것이다. 


이러한 헤더화일은 터보 C에서 30여가지가 있는데, 각 함수들을 30여가지 

기능들로 분류하여 선언해 놓은 것이다. 예를 들어 printf()같은 기본 입 

출력 함수는 stdio.h 화일에 선언되어 있고, 그래픽 관련 함수들은 graph 

ics.h 화일에, 문자열 처리 함수들은 string.h화일에 선언되어 있으며, 수 

학적 처리를 담당하는 함수들은 math.h에 선언되어 있다. 이러한 내장 함 

수들은 여러분들이 실제 프로그램 작성시 필요에 의해서 배울 수 있을 것 

이다. 


하지만, 이러한 내장 함수들도 함수의 개념이 잡혀 있어야만 무리없이 잘 

사용할 수 있다. 그러면, 사용자함수를 제작하는 방법에 대하여 알아보자. 



2. 사용자 함수 


특별한 경우가 아니라면 C에서 말하는 함수는 사용자가 정의한 함수를 가 

리킨다. C의 함수는 모두 횡적인 관계를 유지하고 있어, 함수들이 독립적 

으로 호출, 운용되며, 함수내에서 다른 함수를 호출하는 것은 가능하나 함 

수내에서 다른 함수를 정의할 수는 없다. 

물론 사용자 함수도 내장 함수들 처럼 선언이 되어야 한다. 내장 함수와 

는 달리 사용자가 직접 선언을 해야 한다. 


함수는 하나의 단위 프로그램이므로 필요시 변수를 선언하여 사용할 수 

있다. 이 변수들을 지역변수(local variable)라고 하는데, 함수내에서 선 

언된 변수들은 그 함수내에서만 효력을 가질 뿐 실행이 그 함수를 벗어나 

면 그 값을 상실하게 된다. 변수들의 통용범위에 대해서는 뒷부분에서 자 

세히 설명될 것이므로 우선 사용자 함수 제작법에 대해 알아보자. 



1) 함수의 형식 


type 함수명(가인수 리스트) 

가인수 형선언; 

내부 변수의 선언; 

...... 

...... <=함수의 본체 

return(계산 결과); 


(1) 형 (type) : 함수는 계산된 결과를 되돌려주므로 당연히 그 형 

을 명시하여야 한다. 형언 int, float, double 등 여태 배운 형 

선언 문자이면 된다. 함수의 형이 정수형(int)이거나 되돌려주는 

값이 없을 때에는 형선언을 생략할 수 있다. 되돌리는 값이 없는 

함수일 경우에는 void를 쓰거나 아무 것도 쓰지 않는다. 


(2) 함수명 : 함수명은 변수명의 규칙에 어긋나지 않는 문자열이면 

아무것이나 좋다. 가인수 리스트는 호출 프로그램에서 전달받은 

인수(실인수)와 갯수가 동일해야 하며, 인수가 없는 경우에는 가 

인수 리스트를 생략할 수 있지만 ()자체를 생략하면 안된다. 


(3) 가인수의 형선언 : 가인수가 있는 경우에는 가인수의 형을 선언 

해야 한다. 이 형선언은 main()프로그램에서 변수에 대해 형선언 

을 하는 것과 동일하다. 


(4) 내부변수의 선언 : 함수에서 연산에 필요한 변수들을 선언하여 

쓸 수가 있다. 이 변수들은 그 함수내에서만 통용되므로(지역 변 

수) 다른 함수의 내부 변수나 main()함수의 변수와 같아도 된다. 


(5) return(); : 연산 결과를 주프로그램에 전달할 경우 ()안에 그 

값이나 변수를 기술하면 된다. 전달할 값이 없으면 return()자체 

를 생략할 수 있다. 


<예제1> 3개의 실수 자료를 전달받아 최대값을 구하는 함수를 작성하라. 


<리스트1> float max(a,b,c) /* (1) (2) */ 

float a, b, c; /* (3) */ 

float mx; /* (4) */ 

if (a > b) mx=a else mx=b; 

if (c > mx) mx=c; 

return(mx); /* (5) */ 


위에서 함수명은 max이며 인수로 실수형 a, b, c를 전달받는다. 이 함수 

는 중간 결과를 저장하기 위해 mx라는 변수를 사용하고 있으며, 이 변수의 

형은 실수형으로 선언되어 있다. 


위의 1행과 2행을 아래와 같이 표현할 수 있다. 

float max(float a, float b, float c) 

... 

} <= 프로그래머들은 이 방식을 주로 애용한다. 



2) 함수의 호출 


함수는 일종의 부프로그램으로 스스로 수행되지는 않고 다른 프로그램에 

서 호출해야만 실행된다. 그러므로 주프로그램보다 앞에 있더라도 호출하 

지 않는한 실행되지 않는다. 

함수의 호출은 함수명과 인수만 기술하면 되는데, 식의 일부로도 사용할 

수 있고 호출자체가 하나의 문장을 이루기도 한다. 예를 들어 다음의 문장 

은 함수 max()를 호출하며, 함수에서 계산된 결과를 k에 기억시킨다. 


k=max(x,y,z); 


위에서 x,y,z는 실제 값이 기억된 변수이므로 이들을 실인수라고 하고, 

max()함수의 인수란에 기술된 a,b,c를 가인수라고 한다. 실인수와 가인수 

는 이름이 서로 달라도 되지만 갯수와 형은 일치하여야 한다(당연한 소리) 


<리스트1>에서 작성된 함수를 호출하는 주프로그램을 만들어 보면, 


#include 

float max(float a, float b, float c); 


void main(void) 

float x,y,z,k; 

printf("입력:"); 

scanf("%f %f %f", &x, &y, &z); 

k=max(x, y, z); 

printf("최대값: %f\n",k); 


우선 main()도 함수라는 것을 잊지 말기를 바란다. main()함수가 하는 일 

은 DOS로 부터 필요한 인수들을 받아 자신의 일을 행한 후, 어떤 값을 DOS 

에게로 돌려주는 함수이다. 자세한 것은 8장에 언급되어 있다. 

위에서 void main(void)란 것은 아무 인수도 받지 않도 되돌리는 값도 없 

을 때 void라는 키워드를 쓴다. 


위 프로그램은 3개의 실수 자료를 입력받아 max()함수로 전달하여 연산결 

과를 구해온다. 연산된 결과는 max()함수 자체로 되돌려지므로 이 함수를 

식의 일부로, 혹은 다른 함수의 인수로 사용할 수도 있다. 그러므로 다음 

과 같이 사용하는 것이 허용된다. 


k=max(x, y, z) * 10; 

printf("%f\n",max(x,y,z); 


C에서는 주프로그램인 main()도 하나의 함수로 취급되며, 이 함수는 프로 

그램상 단 한번만 표시되어야 한다. 


함수는 main()함수의 앞에 둘 수도 있고, 뒤에 둘 수도 있다. 함수가 mai 

n()함수의 뒤에 있을 경우에는 프로그램의 첫머리 즉, #include다음, main 

()앞에서 선언을 해야 한다. 함수의 선언은 다음과 같이 함수의 형과 함수 

명, 그리고 (가인수 형선언)을 기술한다. 

그리고, 함수 선언시에는 정의할 때와는 달리 반드시 문장 끝에 ;가 붙는 

다는 것을 까먹지 말기를 바란다. 



<예제2> 인수를 갖지 않으며 되돌리는 값이 없는 함수의 보기 


<리스트2> #include 

#include "myfunc.h" +---- myfunc.h 의 내용 ---+ 

| | 

main() | void afunc(void); | 

{ | void bfunc(void); | 

afunc(); /* 함수 호출 */ +--+----------------------+ 

bfunc(); + void는 생략가능 


afunc() 

printf("함수 A 실행!\n"); 

getch(); 


void bfunc(void) /* 원칙적인 표기: void는 생략가능함 */ 

printf("함수 B 실행!\n"); 


이 예제에서는 함수의 선언을 myfunc.h라는 사용자 헤더 화일에 기술해 

두고 #include문으로 포함시키는 방법을 섰다. < >는 C의 표준 헤더 화일 

을 표시할 때 쓰고, " "는 사용자 헤더화일을 표시할 때 주로 사용한다. 


(내부적으로는 <>는 C의 include디렉토리에서 먼저 화일을 찾고, ""는 현 

재 사용자의 디렉토리(TC를 실행시켰던 디렉토리)에서 먼저 헤더 화일을 

찾는다) 



<예제3> 정수 2의 0부터 10까지의 제곱승을 구하는 프로그램 


<리스트3> #include 


main() 

int m, k, c; 

m=2; 

for (k=0;k <= 10;k++) { 

c=power(m,k); 

printf("%d ^ %d=%d\n",m,k,c); 


power(x,y) /* 정수형이므로 int 생략가능 */ 

int x, y; 

int i,p; 

p=1; 

for (i=1;i <= y; i++) 

p *= x; 

return(p); 


3) 인수의 전달 



주프로그램(main()또는 함수)에서 부프로그램(함수)으로 인수를 전달하는 

방법으로는 두 가지가 있다. 


Call by value(값에 의한 전달) - 주프로그램에서 전달된 인수를 부프로 

그램에서 전달받을 때 인수의 값을 받아오며, 그 값은 실인수와 다른 기 

억장소에 저장된다. 그러므로 함수에서 가인수의 값을 아무리 바꾸더라 

도 실인수에는 아무런 영향을 미치지 않는다. (<예제1>의 경우와 같다) 


Call by Reference(참조에 의한 전달) - 함수에 인수가 전달될 때는 실인 

수가 저장된 메모리의 번지가 전달된다. 그러므로 함수에서는 메모리의 

번지를 참조하여 인수의 값을 꺼내온다. 결국 주프로그램의 실인수와 부 

프로그램(함수)의 가인수는 이름만 다를 뿐 같은 기억장소를 배정받으므 

로 함수에서 가인수의 값을 바꾸게 되면 실인수 자체가 바뀐다. 이러한 

참조에 의한 전달은 기억장소를 절약할 수 있다는 장점과 함께 함수에서 

주프로그램으로 여러 개의 계산 결과를 전달하고자 할 때 유리한 방법이 

다. ( scanf()함수를 상기하라) 


C에서는 참조전달이 허용되지 않으나 메모리 주소 연산자(&)와 포인터 연 

산자를 효과적으로 사용하면 참조전달과 같은 효과를 거둘 수 있다. 


호출측 : func(&a,&b,&c); <= 각 실인수의 주소값을 전달한다 

.... 


피호출측 : func(int *x, int *y, int *z) 

*x=1; *y=2; *z=3; 

... 


위에서 호출측 프로그램에서는 피호출측 프로그램에 인수를 전달할 때 그 

인수가 저장된 주소(&)를 전달하고 있으며, 피호출측 함수에서는 그 인수 

를 포인터(*)로 받아온다. 포인터란 어떤 값이 가리키는 곳(번지)의 내용 

을 나타내므로 포인터 자체의 값은 주소와 일치하며 그 주소에 1,2,3을 할 

당하므로 결국 호출 프로그램의 변수 a, b, c는 각각 1, 2, 3으로 바뀌게 

되는 것이다. 그러므로 계산 결과를 함수의 가인수에 기억시켜 두면 주프 

로그램의 실인수 자체가 바뀌게 되어 여러 개의 계산 결과를 전달할 수가 

있다. 



<예제4> 3개의 정수 자료를 전달받아 크기순으로 나열하는 프로그램 작성 


<리스트4> #include /* 내장 함수를 선언한 화일 포함 */ 

int sort(); /* 사용자 함수의 선언 */ 


main() 

int a,b,c; 

printf("3개의 정수입력: "); 

scanf("%d %d %d", &a, &b, &c); 

printf("\n입력 데이터: %d %d %d\n",a,b,c); 

sort(&a,&b,&c); 

printf("\n정렬 데이터: %d %d %d\n",a,b,c); 


sort(x,y,z) /* 되돌리는 값이 없으므로 형선언 생략 */ 

int *x, *y, *z; 

int temp; /* 지역변수 선언( sort()내에서만 

if (*x < *y) { 통용 */ 

temp=*x; 

*x=*y; 

*y=temp; 

if (*x < *z) { 

temp=*x; 

*x=*z; 

*z=temp; 

if (*y < *z) { 

temp=*y; 

*y=*z; 

*z=temp; 

} /* 실행결과가 인수에 의해 직접 전해 

} 지므로 return() 생략 */ 


3. 기억 부류 (storage class) 


변수란 어떤 자료를 저장하기 위한 기억 장소로서, 구분방식이 두 가지가 

있다. 그 두가지 구분 방식이란, 


그 변수가 프로그램의 전체에 통용되느냐 일부분에 국한되느냐는 활용 범 

위에 따라, 



지역 변수(Local variable)와 전역 변수(global variable)로, 


또 기억장소와 기억방식에 따라, 


자동(auto)변수, 정적(static)변수, 

외부(external)변수, 레지스터(register)변수의 4가지 종류로 나뉜다. 



1) 지역 변수 



하나의 단위 프로그램에서 선언된 변수를 말한다. 지역 변수는 그 단위 

프로그램에서만 통용되며 그 프로그램을 벗어나면 변수의 효력을 상실한다 

. 우리가 이 강좌에서 여태 사용해 온 모든 변수가 바로 지역 변수이며 ma 

in()함수내에서만 사용 가능한 것들이었다. 


지금까지는 모든 것을 main()함수에서만 처리하였으므로 전역 변수이냐, 

지역 변수이냐 하는 것을 의식하지 않아도 되었다. 그러나 이제부터는 함 

수를 이용한 프로그램의 모듈화(단위화=구조화)를 해야하므로 변수의 이런 

특징을 잘 알아야 한다. 


C는 블럭({}로 묶여진 부분) 내부에서 변수를 선언하는 것을 허용하며,이 

렇게 선언된 변수는 블럭내부에서만 통용된다. 블럭이 중첩된 경우에는 맨 

바깥쪽 블럭에서 선언된 변수가 안쪽의 블럭에도 영향을 미치지만 안쪽의 

블럭에서 선언된 변수는 바깥쪽에 영향을 미치지 않는다. 만약 변수명이 

일치할 경우에는 그 블럭에서 선언된 변수가 선택된다. 


블럭A: { ------+ 

int a,b,c; | 

... | 

블럭B: { ----+ | 

int k,b,s; | | 

... | | 

블럭C: { --+ | | 변수의 통용 범위 

int l,m,n; | | | 

... | | | 

} --+ | | 

} ----+ | 

} ------+ 

블럭D: { ------+ 

int a,b,c; | 

... | 

} ------+ 


위의 경우에는 블럭A에서 선언된 변수 b를 블럭B에서도 선언하고 있다. 

이 경우에 블럭A에서 선언된 변수 b는 블럭B에서는 효력을 발휘하지 못 한 

다. 그러므로 블럭이 중첩된 경우에는 변수명이 같지 않도록 주의해야 한 

다. 그러나 블럭이 다를 경우에는 변수명이 동일해도 상관이 없다(블럭A와 

블럭D의 경우). 이와 같이 특정한 지역안에서만 통용되는 모든 변수의 무 

리를 지역 변수라고 한다. 



2) 전역 변수( 외부 변수 ) 



지역 변수와 상대되는 개념으로 전역 변수라는 것이 있다. 이 것은 말 그 

대로 프로그램의 모든 부분에 걸쳐서 영향을 미치는 변수를 말한다. 전역 

변수는 프로그램 첫머리의 사용자 함수를 선언하는 부분에서 선언한다. 

또한 두번째 관점에서의 외부 변수와 전역 변수는 같은 것이다. 


#include <라이브러리 모듈> 

(함수의 선언) -+ 

(전역 변수의 선언) -+---- 일반적으로 *.h화일에 모아둔다. 

main() 

... 


전역(외부) 변수는 모든 함수에서 통용되며 함수 내부에서 그 값을 바꾸 

면 전역(외부) 변수 자체의 값이 바뀌게 된다. 즉, 주프로그램과 부프로그 

램이 변수를 같이 사용하는 것이 바로 전역(외부) 변수이다. 함수에서 전 

역(외부) 변수를 참조할 경우에는 함수의 첫머리 함수명 다음에 변수명을 

같이 기술한다. 


extern type 변수명; 


extern(al)은, 외부에서 선언된 변수임을 나타내는 예약어이다. extern은 

외부 변수가 함수의 뒤에서 선언된 경우에는 꼭 기술해야 되지만 앞에서 

선언된 경우에는 생략할 수도 있다. 그러나 함수가 main() 앞에 오는 경우 

라면 반드시 기술해야 한다. 


초보자의 경우에는 주프로그램을 먼저 기술하고 함수는 나중에 기술하려 

하겠지만 일반적으로 숙달된 프로그래머는 그 프로그램에서 사용할 함수를 

모두 설계해서 기술한 다음 main()은 맨 나중에 배치한다. 처음에는 모든 

변수를 전역 변수로 선언해서 사용하는 편이 프로그램을 작성하기에 편리 

하나, 이런 식으로 전역(외부) 변수를 남용하게 되면 함수의 독립성 즉, 

프로그램의 모듈화에 역행을 하게 되므로 되도록 사용하지 말자. 


예를 들어 앞에서 설명했던 최대값을 구하는 함수(max)의 경우에는 어떤 

프로그램에서든지 곧바로 삽입하여 사용할 수 있으며, 최대값을 구하는 경 

우에는 이 함수를 호출하면 된다. 이 때 계산 결과는 함수 이름(max) 자체 

에 의해 전달되므로 주 프로그램의 변수와 무관하게 되고, 이에 따라 이 

함수만 따로 파일로 만들어 두고 필요할 때 마다 불러내어 사용할수 있다. 

그러나 이 함수를 전역 변수에 의해 값을 전달할 경우에는 모든 프로그램 

의 전역 변수명을 일치시켜야 하므로 비효율적이다. 



<예제5> 전역(외부) 변수 사용 예 보기. 


<리스트5> ==sample1.c 화일== | ==sample2.c 화일== 

-----------------------------+----------------------------- 

#include | sub3() 

int x; /*외부변수 선언*/ | { 

| extern int x; 

main() | x++; 

{ | printf("sub3 x=%d\n",x); 

x=16; | x=200; 

sub1(); | } 

sub2(); | 

sub3(); | sub4() 

sub4(); | { 

} | int x=34; 

sub1() | printf("sub4 x=%d\n",x); 

{ | } 

x++; +----------------------------- 

printf("sub1 x=%d\n",x); | == sample.prj 화일 == 

} +----------------------------- 

sub2() | sample1.c 

{ | sample2.c 

x++; | 

printf("sub2 x=%d\n",x); | 

} | 


이러한 식으로 프로그램을 분할해서 모듈화를 시켜서 분할 컴파일이 가능 

하다. prj화일은 프로젝트 화일이라는 것으로 여러개의 소스 화일을 각각 

컴파일해서 하나의 실행화일로 만들어 주는 역할을 담당하는 화일하다. 

이러한 모듈별 분할 컴파일은 여러분이 64kb를 넘는 대형 프로그램을 작 

성할 때 반드시 필요한 개념이다. 

( 자세한 사항은 각 컴파일러 메뉴얼을 참조하기 바란다 ) 


프로그램의 실행결과는 여러분이 직접 실행해 보길 바란다. 



3) 자동 변수 



자동 변수는 선언된 함수 내부나 혹은 그 함수내의 복문속에서만 효력을 

가지고, 그 블럭이나 함수를 벗어나면 자동으로 소멸되는 지역 변수를 일 

컫는다. 이러한 종류의 변수들은 선언시 메모리를 할당받아 자료를 기억시 

키는데 쓰이지만 사용후에는 메모리에서 완전히 제거된다. 자동 변수는 변 

수의 선언시 형 앞에 auto를 붙이거나 아무 것도 붙이지 않는다. 이 때까 

지 사용해 온 변수들이 지역 변수이자 자동 변수이다. 

자동 변수는 초기화하지 않으면 그 값은 미정이며, 컴파일시 변수의 값이 

저장될 메모리의 위치만 할당할 뿐 초기화는 프로그램 실행시에 한다. 이 

변수는 *그 함수의* 실행이 끝나면 자동적으로 소멸되며, 호출될 때마다 

초기값을 가진다. 



4) 정적 변수 



정적 변수는 프로그램의 종료 후에도 그 값이 소멸되지 않고 보관되며,다 

시 호출될 때는 그 직전의 값을 참조할 수 있다. 

정적 변수의 초기화는 프로그램의 컴파일시에 단 한번 하게 된다. 정적변 

수는 형 앞에 static이라 기술함으로써 선언된다. 예를 들어, 


static int a=0, b=0; 

a =+ 1 ; b += 1; 


에 의해 변수 a,b가 정적 변수로 선언되며, 초기값은 0이 된다. 초기값에 

0을 대입하는 것은 컴파일시에 단 한번하고, 호출시에는 0이 대입되지 않 

는다. 이렇게 선언된 변수는 실행이 끝난 후에도 그 값이 남아있게 된다. 

그러므로 호출될 때마다 변수 a, b가 1,2,3,4,...로 계속 증가된 결과를 

얻게 된다. 

변수를 초기화하는 부분은 위와 같이 반드시 static 다음에 기술해야 한 

다. 만약 행을 바꾸어 기술하면 컴파일시에 초기화하지 않고 실행중에 초 

기화가 되므로 애써 기억한 자료가 다시 초기화되어 버린다. 

정적 변수는 실행된 후에도 메모리에 상주하므로 기억장소 절약이라는 측 

면에서 볼 때 바람직한 형태가 아니므로 꼭 필요한 변수의 경우에만 사용 

하는 것이 좋다. 정적 변수는 지역 변수나 전역 변수에 모두 선언하여 사 

용할 수 있다. 


<예제6> 함수 내부에서 선언하는 static 변수 


<리스트6> main() 

printf("정적변수 자동변수\n"); 

sub(); 

sub(); 

sub(); 


sub() 

static int i=1; 

auto int k=3; /* auto는 보통 생략한다 */ 

printf("i=%d k=%d\n",i,k); 

i++; k++; 


함수 내부에서의 static선언은 그 함수내의 국소적인 변수가 된다.(위의 

예는 지역 정적 변수라고 할 수 있다) 

위에서 k는 자동변수이므로 호출할 때마다 초기화가 되고, i는 정적 변수 

이므로 컴파일시 한 번만 초기화를 행하여 메모리에 초기치 1을 저장하고 

뒤에는 sub가 호출될 때마다 +1씩 값을 증가시켜 변화시켜 간다. 


<예제7> 함수 외부에서 선언되는 static 변수 


<리스트7> static int k=4; 

main() 

printf("main - k=%d\n",k); 

inc(); 

printf("main - k=%d\n",k); 


inc() 

++k; 

printf("sub - k=%d\n",k); 

++k; 


함수 외부에서의 선언은 그 소스화일 내에서는 어디서나 참조가능한 내부 

변수로 된다. (전역 정적 변수) 그러므로 변수값은 프로그램의 종료시까지 

보존된다. 위 프로그램의 실행값은 차례로 4, 5, 6이 된다. 

만일 다른 화일에서 k를 참조하려면, 그 화일의 선두에 extern static in 

t k;를 첨가하면 된다. 


<예제7>의 경우처럼 외부적인 정적 변수 등의 폭 넓은 영역에 걸쳐 참조 

할 수 있는 변수들은 절제해서 사용하여야 한다. auto변수(우리가 지금까 

지 사용한 변수들)를 되도록 많이 사용하여 프로그램의 모듈화, 구조적 특 

징을 손상시키지 않도록 하자. 



5) 레지스터 변수 



레지스터 변수는 기억할 자료의 값을 메모리에 저장하는 대신 직접 CPU내 

의 기억장소인 레지스터(register)내에 기억시키는 변수이다. 이것은 for 

문 등의 제어 변수를 직접 레지스터에 기억시켜 둠으로써 실행 속도를 높 

일 목적으로 쓰인다. 그 형은 int형에 한한다. 사실 함수에서 선언되는 변 

수 중 처음 2개의 int형 변수는 자동적으로 레지스터 변수로 사용이 되므 

로 속도를 요하는 변수(루프안을 도는 변수 등)는 맨 처음에 선언하는 것 

이 유리하다. 


레지스터 변수는 컴파일러에게 지시하는 것이지만 강제적인 것은 아니다. 

레지스터의 갯수가 한정되어 있을 뿐 아니라 또 그 시각에 사용하지 않는 

레지스터가 있는지도 미지수이기 때문이다. 만약 사용 가능한 레지스터가 

없다면 자동 변수로 할당 된다. 


<기억부류 사용예> 

int a; /* 외부변수 a의 정의 겸 선언 */ 

(다른 모듈(-화일)에 알려질 수 있다) 

extern int b; /* 외부변수 b의 선언 */ 

(통상 b는 다른 모듈에 정의되어 있을 것이다) 

static int c; /* 외부 정적변수 c의 정의 겸 선언 */ 

(다른 모듈에는 알려지지 않는다) 

void main(void) 

int d; /* 자동변수 d의 정의 겸 선언 */ 

auto int e; /* " e " (auto는 생략가) */ 

static int f; /* 내부정적변수 f의 정의 겸 선언 */ 

register int g; /* 레지스터 변수 g의 정의 겸 선언 */ 

....... 


<기억부류 선택요령> 참고: Turbo C정복(임 인건 저) 


(1) 피치 못할 경우를 제외하고는 가능한 한 자동변수를 선택 

(2) 외부변수의 사용은 최대한 자제하고 공용성이 매우 높은 변수 

에 한해 외부변수로 정의한다. 

(3) 초기화가 꼭 필요하거나 공용성을 가지는 배열은 주로 외부형으로 

정의한다. 

(4) 모든 외부변수(정적변수 포함)는 가능한 한 초기화를 한다. 

(5) 외부변수는 가능한 한 읽기 전용으로 한다. 

(6) 특정 외부 변수의 값을 변경하는 함수는 가능한 한 하나의 함수로 

제한한다. 

(7) 자동변수의 값을 보존할 필요가 있을 경우 내부정적변수로 정의 

(8) 프로그램이 하나의 모듈로 구성되어 있을 경우 외부정적변수를 사용 

할 필요는 전혀 없다. 




4. 예제들 


<예제8> 임의의 정수 n을 입력받아 n의 계승(!)을 구하는 프로그램 작성. 


<리스트8> 1: #include 

2: double fact(); /* 함수 선언 */ 

3: 

4: main() 

5: { 

6: int n=0; /* 내부변수의 선언 */ 

7: do { 

8: printf("양수 입력:"); 

9: scanf("%d",&n); 

10: if (n<0) return(0); /*입력된 수가 0보다 작*/ 

11: fact(n); /* 으면 프로그램 종료 */ 

12: printf("\n"); 

13: } while (1); /* 조건이 1(참)이므로 무한루프 */ 

14: } 

15: 

16: double fact(k) 

17: int k; /* 인수의 형 선언 */ 

18: { 

19: register int i; /* 레지스터 변수 선언 */ 

20: double s=1; 

21: for (i=1;i <= k;i++) { 

22: s *= i; 

23: printf("%3d! => %16.0f\n",i,s); 

24: } 

25: return(s); 

26: } 


10행에서 return()은 함수의 계산 결과를 호출 프로그램으로 되돌리는 명 

령어이다. return(0)은 그 시점에서 의미가 없는 0을 되돌림으로서 함수 

수행을 종료시키는 명령이다. main()도 일종의 함수이므로 return(0)을 사 

용할 수가 있다. 



<예제9> 두 점 (x1,y1),(x2,y2) 사이의 직선거리는 아래에 식에 의하여 

구할 수 있다. n개의 점을 입력받아 각각의 점을 직선으로 연결 

할 때의 직선거리의 합을 구하라. 

______________________ 

( 두 지점간 거리=/ (x1-x2)^2 + (y1-y2)^2 ) 


<리스트9> 1: #include 

2: #include 

3: 

4: float length(a1,b1,a2,b2) 

5: int a1,b1,a2,b2; 

6: { 

7: float len; 

8: len sqrt(pow(a1-a2,2.)+pow(b1-b2,2.); 

9: printf(" Length=%f\n",len); 

10: return(len); 

11: } 

12: 

13: main() 

14: { 

15: int x1,x2,y1,y2,n,i; 

16: float s=0; 

17: printf("Input Number of Points:"); 

18: scanf("%d",&n); 

19: printf("1st Point (x1,y1) :"); 

20: scanf("%d %d", &x1, &y1); 

21: for (i=2; i <= n;i++) { 

22: printf("%dst Point (x%d,y%d) :",i,i,i); 

23: scanf("%d %d", &x2,&y2); 

24: s += length(x1,y1,x2,y2); 

25: x1=x2; 

26: y1=y2; 

27: } 

28: printf("\n Total Length=%f\n",s); 

29: } 


2행: 외부라이브러리의 선언 ( 제곱근을 구하는 함수 pow()의 선언 ) 

8행: 두 지점간의 거리 계산 / 그냥 2를 쓰지않고 2.을 쓴 이유는 pow() 

함수의 인수의 형이 실수이기 때문이다. 

10행: 계산 결과를 되돌림 

15,16행: 내부 변수의 선언 

22,23행: 2번째 이후의 좌표 입력 

24행: 두 지점간의 거리 누적 

25,26행: 다음 지점을 입력하기 위한 준비 



함수는 프로그램을 기능별로 묶는 모듈러 프로그래밍을 가능하게 한다. 

프로그램은 물론 계산 결과가 정확해야 하지만 같은 계산 결과를 제공하 

는 것이라면 프로그램을 이해하기 쉽고 또, 디버깅하기 쉽도록 작성하는 

것이 좋다. 


2장에서 언급하였듯이 C에서는 외부 라이브러리를 컴파일러 제작사에서 

제공하는 것은 물론 사용자 자신이 작성하는 것도 허용한다. 위에서 작성 

했던 함수들을 적당한 이름으로 디스크에 수록하여 둔다면 그 함수가 필요 

한 경우에 #include문에 의해여 소스상에 삽입하여 실행할 수 있다. 그러 

므로 C에서 표준 라이브러리에 없는 함수라 할 지라도 자신이 설계해 둔다 

면 다른 여타 언어보다 훨씬 더 풍부한 함수 및 처리 체계를 구축할 수가 

있을 것이다. 라이브러리를 만드는 법은 참고 서적을 참조하기 바란다. 




다음 장에서는 드디어 C언어의 첫 난관인 배열과 포인터에 대해서 알아볼 

터인데 여러분들은 워밍 업을 충분히 해 두는 게 좋을 것 같다. 하지만 

그렇게 두려워 하지 않아도 될 것이다. 지금까지 쫓아온 그대에게는 끈기 

가 있을 테니까 말이다... 


 



반응형

'IT > Programming' 카테고리의 다른 글

제 7장 - 구조체와 공용체  (0) 2016.03.22
제 6장 - 배열과 포인터  (0) 2016.03.22
제 4장 - 연 산 자  (0) 2016.03.22
프로그래밍 기초 3  (0) 2016.03.22
제 2장 - C의 입출력 함수 및 수의 표현  (0) 2016.03.22
Comments