C 언어에서도 다른 언어와 마찬가지로 함수(function)를 사용할 수 있다. 필요에 따라 사용자가 함수를 만들어서 프로그램 내에서 이를 이용하면 된다. 그러나 c언어는 모든 모듈이 함수 그 자체로 구성되기 때문에 다소 혼동이 올 수 있으나 리턴(return)되는 값의 유무에 따라 이를 구분할 수 있을 것이다. 이번호에서는 C언어에서의 함수선언과 함수내에서 변수가 어떻게 이용되는지 살펴보겠다.
C언어의 함수
일반적으로 C 언어는 내장 함수를 갖고 있다. printf나 main 등도 그 중 하나이다. 그중 제곱근을 계산하는 함수 sqrt를 사용하여 1부터 10까지의 정수의 제곱근을 출력해보자. 그리고 Newton Raphson 법에 의한 제곱근을 사용자가 정의한 함수를 이용하여 값을 구해보자. Newton-Raphson 법에 의한 제곱근 식은 설명하지 않고 함수로 구성할 때의 구조를 설명하겠다. (프로그램1)은 제곱근을 구해주는 함수를 사용한 예제이다.
C언어에는 다른 언어에서 볼수있는 서브루틴(subroutine)과 같은 것이 없어 프로그램은 모두 함수의 집합으로써 취급된다. 함수라는 것은 독립성을 갖는다. 독립성이라고 하는 것인 함수가 하나의 모듈로 구성되어 함수간 독립이 유지된다는것을 의미한다.
독립성이 있기 때문에 함수간에 값을 주고 받을 때에는 사용하는 모든 데이타에 대해 데이타형을 선언해야 한다. 예를 들어 (프로그램1)에서 main이라는 함수로 부터 sqrt 혹은 sqrt1 이라는 함수를 호출할 때에 상대방과의 접속조건에 맞는 데이타형을 주고 받을 필요가 있다. sqrt1도 그 데이타를 double형으로 받고 있다. 먼저 sqrt1에 대해 설명해보자.
함수의 호출은
sqrt 1 (식) ;
의 꼴로 되어 있다. 이렇게 하면 함수 sqrt1에 식의 값이 전해진다. 하지만 sqrt1은 전해진 값을 어떠한 형태로 받을 것인가를 결정해야 한다. 따라서 전해받을 함수를 만드는 시점에서 받을 데이타형을 설정해 둘 필요가 있다.
예제의 경우, sqrt1 쪽이 double 형으로 데이타를 받는 것으로 미리 설정되어 있으므로 호출하는 쪽은 이에 맞추어 cast를 사용하여
sqrt1(double(i));라 한다.
함수 sqrt1 쪽으로 부터 무엇인가 데이타가 돌아올 때에는 그 데이타를 받을 준비가 필요하다. double 형의 데이타를 호출하는 쪽이 받는다고 하면 그 정보를 알려줄 필요가 있다. 따라서
doulbe sqrt1( );이라고
선언해 둔다. 이것으로 호출하는 쪽의 준비는 완료된다.
(프로그램1)의 경우 sqrt1의 설정은
로 되어있다.
제일 처음의 함수명 앞에 있는 함수의 데이타 형은 함수 본체에서 계산된 결과가 return 함수에 의해 되돌려 주는 데이타 형을 지정한다.
이 데이타 형이 int형인 경우는 이를 생략할 수 있다. 함수명은 호출될 때의 함수의 이름을 나타내고 있다. 함수에 인수가 없을 때에는 ( )속에 아무것도 쓰지않아도 되지만 ( )는 생략해서는 안된다. 그 다음에 인수의 각 데이타형을 선언하고 블럭에서의 처리 내용을 쓴다. 마지막의 return은 원래의 함수명에 되돌려주는 값이 있을때만 사용한다. 아무값도 둘려주지 않는 경우는 사용하지 않아도 된다.
C언어의 함수 호출에서 중요한 것은 특별한 지정이 되어있지 않으면 C언어의 함수 인수가 대부분 값에 의해 전달된다는 것이다.
(프로그램2)와 그 실행예를 살펴보자. 함수 a내에서 출력된 값만 보더라도 함수내부에서 변화된 값이 main의 변수 값에 전혀 영향을 미치지 않음을 알 수 있다.
(프로그램2)에서는 변수 i(값이 123이다)와 j(값이 456이다)를 함수 a에 값을 건네주고 있다. 함수a에서는 x, y 로 i, j 값을 건네받고 이를 출력한뒤 x=2; y=1; 순으로 새로운 값을 대입하여 또 같은 printf 문으로 값을 출력하고 있다. 그리고 원래의 함수 main으로 되돌아 갔을 때에는 i,j에 아무런 값의 변화를 주지 않고 있다. 이때 i와 j의 값을 a함수내에서 변화시키려면 (프로그램3)과 같이 작성해야 한다.
(프로그램3)에서는 함수 main의 a(&i, &j)로 함수 a에 i와 j의 실인수의 값이 아닌 기억된 곳의 번지를 전해주고 있다. 함수 a에서는 선언
int * x, * y;
즉 x,y가 가리키는 곳은 int 데이타가 기억된 곳이다. 따라서 그 주소의 내용을
*x=2; *y=3; 이라 하면
주소의 내용, 다시말해 i와 j의 값 그 자체가 변하게 된다.
이런 예로 대표적인 함수는 scanf 함수이다. scanf 함수는 본체에 건네주는 데이타가 하나 이상이 될 수 있기 때문에 배열의 사용에 있어서는 제일 처음 데이타가 기억된 기억번지만 건네주면 된다.
변수가 갖는 유효 범위
C언어에서는 블럭내에서 선언되어 사용되는 변수가 있으며 이들은 외부변수와 내부변수로 나뉜다. 외부변수는 블럭의 외부에서 선언되고 내부변수는 그 블럭내에서 선언된 것이다. 내부변수는 그 블럭안에서만 사용할 수 있고 그 블럭을 빠져나오면 전혀 사용할 수가 없다. 일반적으로 블럭내에서만 사용되는 변수는 될 수 있는 한 내부변수로 사용하는 것이 바람직하다.
외부변수는 블럭의 외부에서 선언되고 자신의 블럭 및 그 내부에 포함된 블럭에 대해 유효하다. 따라서 main함수의 외부에서 선언된 변수는 모든 블럭에서 사용할 수 있다.
(프로그램4)는 변수의 유효범위를 보여주기 위한 예이다.
(프로그램4)에서는 main함수의 외부에서 i=0;로 대입하고 있다. 이것은 이후의 모든 블럭에 대해 유효하다. 다음에 main 함수안의 블럭 1에서 i=123;로 대입하고 있다. 이것은 블럭 1에 포함된 블럭에 대해 유효한 외부 변수가 된다. 그러나 블럭1을 빠져나오면 사용할 수 없다. 다시 말하면 블럭1에서만 사용가능한 내부변수이다.
블럭2에서 i=456;로 대입하고 있다. 이것은 블럭2에 포함된 블럭에 대해 유효한 내부 변수이다.
블럭 3에서 i=789;로 대입하고 있다. 이 i는 블럭 3에서 선언되어 있지 않으므로 블럭 2의 i에 대한 대입으로 처리된다.
블럭 3을 빠져나와 블럭 2로 옮겨져도 블럭 3에서의 i=789에 의해 그후로부터의 i는 당연히 789가 된다. 또 블럭 2를 빠져나오면 지금까지의 변수는 사용할 수 없게되고 이전의 블럭 1에서 선언한 변수 i를 사용할 수 있다.
변수의 분류
C언어의 변수는 그 사용에 따라 다음과 같이 분류된다.
광역적 변수란 main함수 앞에서 선언되고 모든 블럭에 대해 유효하다. 정적 변수는 해당 블럭이 실행될 때마다 생겨나고 없어지는 것이 아니라 변수가 한번 선언되면 그 블럭내에서는 계속 유효한 변수이다. 선언은 static int i;와 같이 한다. 이러한 정적변수는 내부변수로 취급할 수 있다. 또 내부변수의 자동변수는 auto int i;와 같이 한다. 이때 auto 는 생략 가능하고 곧바로 변수의 데이타형을 지정한 경우는 모두 자동변수로 처리된다. 이러한 자동변수 선언으로 변수는 스택(stack) 상에서 동적으로 지정된, 이것은 앞에서도 말한 것과 같이 블럭이 실행될 때마다 선언되고 블럭으로부터 빠져나오게 되면 자동적으로 없어지는 것이다.
레지스터 변수는 기계어에서 레지스터를 사용해 데이타를 처리하듯이 직접 레지스터 수준의 데이타 기억형태를 이용할 수 있는 것이다. 따라서 빈번하게 사용되는 변수들은 레지스터 변수로 선언해두면 프로그램의 실행속도가 빨라지게 된다. 그러나 시스팀에 따라 레지스터 변수의 개수가 다르기 때문에 사용시에는 매뉴얼을 먼저 참조하여야 한다.
선언은 register int i;와 같이 한다.
(프로그램5)는 정적변수의 사용예를 보인 것이다.
함수에 있어서의 재귀적 호출
다음과 같은 함수를 C언어로 작성해 보자.
(프로그램6)은 위 함수를 C언어로 작성해 놓은 것이다. 함수를 정의하는 과정에서 자신의 함수를 호출하는 식으로 프로그램이 구성되어 있다. 이러한 호출을 재귀적 호출(recursive call)이라 부른다.
물론 위 함수를 (프로그램7)로도 처리할 수 있다. 일반적으로 재귀적 호출을 사용하면 프로그램이 간결해지는 경우가 많으나 그 계산 속도는 반드시 빨라진다고 말할 수 없다.
지금까지 변수의 유효범위와 함께 함수 사용예를 다루었다. (예제1)~(예제3)은 다른 예를 보인 것이며 실행결과를 통해 함수사용을 이해하기 바란다.
<;예제1>;
문자열 u= "abcdefghijklmnopqrstuvwxyz"가 있다. 이것을 문자열 v에 복사하라. 이를 위해 함수 strcpy를 작성하라.
<;예제2>;
주어진 문자열
u = "How to delete spaces in string" ;
가 있다. 이 문자열 중에서 빈칸을 제거하고, 임의의 문자열에 대입하는 함수 delsp를 작성하라.
<;예제3>;
문자열u와 문자열 v를 결합하는 함수 strcat를 작성하라.
u = "Happy birthday to" ;
v = "you" ;