Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 6 – 함수와 프로그램 구조 Outline 6.1 함수 정의, 호출 및 선언 6.2 return문

Similar presentations


Presentation on theme: "Chapter 6 – 함수와 프로그램 구조 Outline 6.1 함수 정의, 호출 및 선언 6.2 return문"— Presentation transcript:

1 Chapter 6 – 함수와 프로그램 구조 Outline 6.1 함수 정의, 호출 및 선언 6.2 return문
6.1 함수 정의, 호출 및 선언 6.2 return문 6.3 값 호출 6.4 블록 구조 6.5 기억 클래스(storage class) 6.6 수학 함수 6.7 재귀 함수 6.8 함수를 매개 변수로 사용하기 6.9 함수 main()의 매개 변수 void 형 지정 함수 6.11 전처리기 요약 6.13 예제

2 6.1 함수 정의, 호출 및 선언 자료형 함수명(인수_리스트) { 선언문들 문장들 } 함수의 일반 형식
6.1 함수 정의, 호출 및 선언 함수의 일반 형식 자료형 함수명(인수_리스트) { 선언문들 문장들 } 자료형(data type) 함수가 반환하는 값이 갖는 자료형 생략시 int형 자료를 반환한다. 함수명 임의의 식별자를 함수의 이름으로 사용할 수 있다. 인수 리스트 자료형과 인수명을 콤마로 분리하여 열거 (예) int n, int m 인수가 없으면 void를 사용하거나 비워둔다. 선언문들 선언문을 열거한다. (예) int i; double x;

3 6.1 함수 정의, 호출 및 선언 (예 6.1) n부터 m까지의 합 구하는 함수 자료형 : int 함수명 : add_n2m
6.1 함수 정의, 호출 및 선언 (예 6.1) n부터 m까지의 합 구하는 함수 자료형 : int 함수명 : add_n2m 인수_리스트 : int n, int m 선언문들 : int i, sum = 0; 문장들 : for문과 return문을 사용하였다. 함수 add_n2m /* n부터 m까지의 합 구하는 함수 */ int add_n2m(int n, int m) 3 { int i, sum = 0; for (i = n; i <= m; i++) sum = sum + i; return sum; }

4 6.1 함수 정의, 호출 및 선언 (예 6.1 계속) add_n2m 함수의 호출 인수 리스트의 또 다른 표현법
6.1 함수 정의, 호출 및 선언 (예 6.1 계속) add_n2m 함수의 호출 /* 함수 add_n2m의 호출 */ 10 int main(void) 11 { int k; 13 k = add_n2m(3, 6); printf(“결과 = %d\n”, k); 16 } output 결과 = 18 인수 리스트의 또 다른 표현법 괄호 안에 인수명만 열거하고, 함수 몸체 직전에 자료형과 인수명 열거 즉 다음 두 가지의 함수 선언 방식은 동일하다. int add_n2m(int n, int m) { . . . } int add_n2m(n, m) int n; int m; { . . . }

5 6.1 함수 정의, 호출 및 선언 (예 6.2) 인수 없는 함수 함수명 뒤의 (void)나 ()는 인수가 없음을 나타냄.
6.1 함수 정의, 호출 및 선언 (예 6.2) 인수 없는 함수 함수명 뒤의 (void)나 ()는 인수가 없음을 나타냄. 앞의 void는 반환값이 없다는 뜻 함수 welcome_message /* File : prog6-2.c 인수 없는 함수 */ 3 void welcome_message(void) 5 { printf(“%s\n%s\n%s\n%s\n\n”, “% **********************”, “% *** Welcome to **”, “% *** Seoul, Korea **”, “% **********************”); 11 }

6 6.1 함수 정의, 호출 및 선언 (예 6.2 계속) welcome_message 함수의 호출 output
6.1 함수 정의, 호출 및 선언 (예 6.2 계속) welcome_message 함수의 호출 12 /* 인수 없는 함수의 호출 */ 13 int main(void) 14 { welcome_message(); 16 } output ********************* ** Welcome to ** ** Seoul, Korea **

7 6.1 함수 정의, 호출 및 선언 (예 6.3) 지역 변수(local variable)와 전역 변수(global variable) 함수 iavg의 지역변수는 그 함수에서 정의된 변수 z뿐만 아니라 인수로 복사되어 넘어 온 x와 y도 포함된다. /* (1) */ /* x, y, z는 함수 내부에서 선언되었으므로 지역 변수 */ void iavg(int x, int y) { int z; z = (x + y) / 2; printf(“%d\n”, z); } 지역 변수 전역 변수 /* (2) */ /* i는 함수 밖에서 선언되었으므로 전역 변수 */ int i = 3; int main(void) { printf(“i = %d\n”, i); }

8 6.1 함수 정의, 호출 및 선언 (예 6.4) double 형의 값을 반환하는 함수
6.1 함수 정의, 호출 및 선언 (예 6.4) double 형의 값을 반환하는 함수 맨 앞의 double은 반환하는 값의 자료형이다. 결과 값을 지역변수 z에 저장한 후 그 값을 반환 함수 avg /* File : prog6-4.c double 형을 반환하는 함수 */ 3 double avg(double x, double y) 5 { double z; z = (x + y) / 2; return z; }

9 6.1 함수 정의, 호출 및 선언 (예 6.4 계속) 함수 avg의 호출 [해 설] output
6.1 함수 정의, 호출 및 선언 (예 6.4 계속) 함수 avg의 호출 10 /* 함수 avg의 호출 */ 11 int main(void) 12 { double res; res = avg(85.0, 90.0); printf(“평균=%.1lf\n”, res); 16 } [해 설] output 14: 함수 avg를 호출하여 반환되는 값을 res에 배정(즉 저장) 15: %.1lf는 소수점 미만 1자리로 long float(즉 double)형 자료를 출력하라는 뜻. 평균=87.5

10 6.2 return 문 return 문의 두가지 형식 return; /* (1) */ return 수식; /* (2) */
(1) 반환값 없이 호출 환경으로 돌아가기 (2) 수식을 평가하여 호출 환경으로 반환 return; /* (1) */ return 수식; /* (2) */ 함수 avg3 (예 6.5) 함수형 지정과 return 문 avg3는 3 정수의 평균을 double형으로 반환한다. 반환할 x가 float 형이므로 double형으로 바뀌어 반환됨 /* File : prog6-5.c 함수형 지정과 return 문 */ 3 double avg3(int i, int j, int k) 5 { float x; x = (i + j + k) / 3.0; return x; }

11 6.2 return 문 (예 6.5 계속) 함수 avg3의 호출 [해 설] output
11 int main(void) 12 { double res; int a = 85, b = 90, c = 95; res = avg3(a, b, c); printf(“평균=%.1lf\n”, res); 17 } [해 설] output - 함수의 인수는 매개변수라고도 부른다. - 함수 내에서 사용되는 매개변수(즉 i, j, k)는 형식 매개변수, 호출한 환경에서 제공되는 매개변수(즉 85.0, 90.0, 95.0)는 실 매개변수라 한다. - %.1lf는 소수점 미만 1자리로 long float(즉 double)형 자료를 출력하라는 뜻. 평균=90.0

12 6.3 값 호출(call by value) 참조(에 의한) 호출(call by reference)
호출 시 제공하는 실 매개변수의 값을 복사(copy)하여 함수내의 형식 매개변수에 전달하는 방식 함수에서는 원본이 아닌 복사본을 가지고 동작되므로, 호출 환경의 원래 값은 변경될 수 없음. C 언어의 함수 호출 시 기본적으로 call by value로 동작됨. 참조(에 의한) 호출(call by reference) 함수 호출 시 인수의 주소를 넘겨주는 방식으로, C 언어의 기본 방식은 아니다. call by reference = call by address = call by location

13 6.3 값 호출(call by value) (예 6.6) 값 호출 방식의 동작 함수 호출 시 i값 4가 함수내의 k로 복사됨
줄13에서 k값이 37로 변경된다. 그 변경된 k값이 줄 15에서 반환됨 함수내의 k값은 복사본이므로, 원본 i값은 그대로 4이다. /* File : prog6-6.c 값호출 방싱의 동작 */) #include <stdio.h> int main(void) { int i = 4, j; j = try_to_change_it(i); /* 반환된 값 37을 j에 배정 */ printf(“%d %d\n”, i, j); /* 4 37 이 출력됨 */ } 10 try_to_change_it(int k) /* 함수의 반환형이 생략되면 int형이 반환됨 */ 11 { printf(“%d\n”, k); /* 4 가 출력됨 */ k += 33; /* k의 저장값이 변경됨 */ printf(“%d\n”, k); /* 37 이 출력됨 */ return k; /* 값 37을 반환함 */ 16 } 함수 try_to_change_it 실 매개변수 (actual parameter) 호출 시 ( )속에 변수명 또는 값을 표기 형식 매개변수 (formal parameter) 피호출 함수의 ( )속에 있는 변수 cf. 호출함수(calling fct.) 피호출함수(called fct.)

14 6.3 값 호출(call by value) 값 호출 방식의 절차 각 실 매개변수 위치의 수식이 평가된다.
각 수식의 값이 해당되는 형식 매개변수에 복사되어 전달된다. 함수 몸체부의 각 문장이 순서대로 집행된다. return 수식; 이 수행되면, 수식의 값을 함수의 자료형으로 변환하여 호출환경으로 반환한다. return; 문이 수행되거나, 함수 몸체의 마지막 문장이 수행되었다면, 반환 값 없이 호출환경으로 돌아간다.

15 6.4 블록 구조 (예 6.7) 영역 규칙 C언어의 블록 = {와 } 사이에 다수의 문장을 늘어놓은 중문
6.4 블록 구조 C언어의 블록 = {와 } 사이에 다수의 문장을 늘어놓은 중문 변수의 영역(scope) = 그 변수가 영향을 미치는 범위 영역 규칙(scope rule) 선언된 블록 안에서만 접근 가능하다. 안쪽 블록과 바깥쪽 블록에 동일한 이름의 변수가 존재할 경우 안쪽 블록의 변수만이 접근 가능하다. 전역 변수는 가장 바깥쪽 블록의 변수로 취급된다. PL/I이나 Pascal 언어에서는 함수자체도 블록으로 인정된다. (예 6.7) 영역 규칙 /* 예 6.7 (1) */ int sp = 0; double val[100]; /* sp와 val은 외부(external) 전역 변수로 다음의 함수 push(), pop(), clear() 내부에서도 사용될 수 있으며, 더 이상의 선언이 필요 없다. */ double push(f){ } double pop() { } clear(){ }

16 6.4 블록 구조 (예 6.7 계속) 영역 규칙 편의상 다음의 바깥쪽 블록을 A, 안쪽 블록을 B라 하자.
6.4 블록 구조 (예 6.7 계속) 영역 규칙 편의상 다음의 바깥쪽 블록을 A, 안쪽 블록을 B라 하자. 블록 A에서 정의된 변수 a가 안쪽블록 B에서 재정의되어, 바깥쪽 변수는 숨겨진다(hidden 또는 masked된다고 함). 블록에서 벗어날 때 블록 내에서 정의된 모든 변수들이 차지하는 메모리는 반납된다. /* 예 6.7 (2) */ . . . { /* 바깥쪽 블록 A의 시작 */ int a = 5; printf(“%d\n”, a); /* 5가 출력됨 */ { /* 안쪽 블록 B의 시작 */ int a = 2; printf(“%d\n”, a); /* 2가 출력됨 */ } /* 안쪽 블록 B를 닫음 */ printf(“%d\n”, ++a); /* 6이 출력됨 */ }

17 6.4 블록 구조 (예 6.8) 내포된 블록(nested block)에서의 영역 규칙
6.4 블록 구조 (예 6.8) 내포된 블록(nested block)에서의 영역 규칙 다음은 깊이가 3인 블록 구조이다. 가장 바깥쪽 블록을 블록1, 그 안쪽을 블록2, 가장 안쪽을 블록3이라 부르자. 블록1에서 정의된a는 안쪽 블록에서 재정의되지 않았으므로 블록1 내의 어디서나 사용 가능하다. 블록1의 변수 b와 c는 블록2에서 재정의되었으므로 블록2내부에서는 사용할 수 없다. 단 블록2를 벗어나면 다시 사용 가능하다. 블록3에서 다시 c를 재정의한다. 블록2의 c는 블록3에서는 사용할 수 없다.

18 6.4 블록 구조 (예 6.8 계속) /* 예 6.8 */ . . . { int a = 5, b = -3, c = -7;
6.4 블록 구조 (예 6.8 계속) /* 예 6.8 */ . . . { int a = 5, b = -3, c = -7; printf(“%d %d %d\n”, a, b, c); /* 5 –3 7 */ { /* 블록 2의 시작 */ int b = 8; float c = 9.9; printf(“%d %d %.1f\n”, a, b, c); /* */ a = b; { /* 블록 3의 시작 */ int c; c = b; printf(“%d %d %d\n”, a, b, c); /* */ } /* 블록 3를 닫음 */ printf(“%d %d %.1f\n”, a, b, c); /* */ } /* 블록 2를 닫음 */ printf(“%d %d %d\n”, a, b, c); /* 8 –3 -7 */ }

19 6.4 블록 구조 (예 6.9) 병행 블록(parallel blocks)
6.4 블록 구조 (예 6.9) 병행 블록(parallel blocks) 동일한 블록 내 같은 깊이(레벨)로 내포되어 있는 블록들 다음에서 블록 A와 블록 B는 서로 병행 블록이다. 2 병행 블록끼리는 서로의 정보를 알지 못한다. 왜냐하면 블록 A를 벗어날 때 그 블록에서 정의된 모든 변수들이 차지한 메모리가 반납되기 때문이다. /* 예 6.9 병행 블록 */ . . . { int a, b; /* (1) */ printf(“%d %d %d\n”, a, b, c); { /* 블록 A의 시작 */ float b, x, y; . . . /* (1)의 int a는 사용 가능하나, (1)의 int b는 숨어 버림 */ } /* 블록 A를 닫음 */ { /* 블록 B의 시작 */ int a; char c, d; . . . /* (1)의 int b는 사용 가능하나, (1)의 int a는 숨어 버림 */ } /* 블록 B를 닫음 */ }

20 6.5 기억 클래스(storage class) [기억_클래스_지정자] 자료형_명 변수명 [, 변수명, . . .];
변수나 함수의 성질 자료형 : 자료의 크기와 저장형태를 규정 char, short, int, long, float, double 기억 클래스(storage class) : 자료의 생존시간, 유효한 위치 기억 클래스 지정자 : auto, static, extern, register 자료형 선언 시 앞에 추가 가능하다. [기억_클래스_지정자] 자료형_명 변수명 [, 변수명, . . .];

21 6.5 기억 클래스(storage class) 기억 클래스 auto(자동) 블록 내부에서 선언되어 그 블록을 벗어나면 소멸됨
블록 내부에서 선언되어 그 블록을 벗어나면 소멸됨 블록 내에서는 default로 auto임 블록 내에서 기억클래스를 생략하면 auto로 간주됨 다음 두 가지 선언은 동일하다. 기억 클래스 auto { auto char c; auto int i, j, k; . . . } { char c; int i, j, k; . . . }

22 6.5 기억 클래스(storage class) 변수의 초기화 C 언어의 변수는 초기화해야 한다.
Java등의 언어는 자료형에 따라 초기값이 자동으로 결정된다. 가령 int는 0으로 float는 0.0으로 초기화가 일어난다. C 언어에서는 이같은 초기화가 보장되지 않는다.

23 6.5 기억 클래스(storage class) (예 6.10) auto 변수와 스택 /* 예 6.10 자동 변수와 스택 */
int main(void) { int i, j; func1(); func2(); } func1() int x, y; func2() int k, l;

24 6.5 기억 클래스(storage class) (예 6.10 계속) auto 변수와 스택
함수내의 자동변수는 함수 호출 시 스택에 push되어 동작하나, 함수에서 반환하면 스택에서 pop되어 사라진다.

25 6.5 기억 클래스(storage class) 기억 클래스 static(정적) 선언된 블록을 벗어나도 그 변수 값이 보존됨
블록 밖에서는 사용할 수 없음. 이것이 전역변수와 다른 점이다. 그러나 해당 블록이 다시 호출되면 원래의 변수 값을 그대로 사용할 수 있음

26 6.5 기억 클래스(storage class) (예 6.11) 정적 변수(내부형)
인수로 넘어 온 p1과 p2는 자동변수로 함수 sub를 벗어나면 그 값이 소멸됨. si와 ss는 정적 변수이므로 sub를 벗어나기 직전에 저장된 값이 sub를 재 호출하면 유지됨 즉 sub를 처음 호출시에는 si의 값이 0으로 시작하나, 두번째 호출시에는 10으로 시작됨.(세번째 호출시에는 20으로 시작) 만약 si가 static변수가 아니라 auto변수였다면 sub의 호출 시마다 0으로 시작할 것이다. /* 예 6.11 정적 변수(내부형) */ sub(int p1, int p2) { static int si = 0; static short ss; . . . si += 10; } 기억 클래스 static

27 6.5 기억 클래스(storage class) (예 6.12) 정적 변수(외부형)의 예(1)
si와 ss는 정적 변수이며, 함수 안쪽에서 정의되지 않고 sub1과 sub2 함수 앞에서 선언되었다. 즉 si와 ss는 그 뒤의 함수들(sub1과 sub2)에서 사용 가능한 정적 변수이다. 분할 컴파일로 다수의 source file 된 C 프로그램을 컴파일할 경우 외부형 정적 변수는 다른 file 의 함수에서는 사용될 수 없다. /* 예 6.12 정적 변수(외부형) */ static int si; static short ss; sub1(int p1, int p2) { . . . } sub2(int p3) 기억 클래스 static (외부형)

28 6.5 기억 클래스(storage class) (예 6.13) 정적 변수(외부형)의 예(2)
다음에서 정적 변수 p는 함수 f1 뒤에서 선언되었으므로, 함수 f2와 f3에서 사용될 수 있다. f1에서는 사용가능하지 않다. /* 예 6.13 정적 변수(외부형) */ f1() { . . . } static int p; f2() /* p 는 이곳에서 사용될 수 있다 */ f3() 기억 클래스 static (외부형)

29 6.5 기억 클래스(storage class) (예 6.14) 함수명의 static 선언
static 함수로 선언하면, 분할 컴파일의 경우에 이 원시 file내에서만 사용 가능한 함수가 된다. 즉 다른 file 에는 비공개 함수가 된다. /* 예 6.14 함수명의 static 선언 */ static sub(int p1) { . . . } static 함수

30 6.5 기억 클래스(storage class) 기억 클래스 extern(외부)
정적 변수의 참조 영역은 한 개의 컴파일 단위에서만 가능 외부 변수로 정의하고, 이를 컴파일 단위가 다른 함수에서 외부 참조(external reference)로 접근할 수 있다. 전역변수나, 함수 등이 default로 extern이다.

31 6.5 기억 클래스(storage class) (예 6.15) 동일한 프로그램에서의 외부(external) 변수
전역 변수는 외부 변수이다. 전역 변수로 정의되면 그 후에는 그 file 어디에서나 접근 가능하다. func()에서 전역변수a에 저장된 100이 출력된다. /* 예 6.15 동일한 프로그램(모듈)에서의 외부 변수 (1) */ int a; /* 전역 변수 */ int main(void) { a = 0; func(); printf(“%d\n”, a); /* 100이 출력됨 */ } func() a = 100;

32 6.5 기억 클래스(storage class) (예 6.15 계속) 동일한 프로그램에서의 외부(external) 변수
함수 f()에서 변수 y와 z가 재정의되어 전역 변수 y와 z는 감추어진다. main에서의 double f(); 함수 프로토타입으로, 아직 정의되지않고 int이외의 형을 반환하는 함수 f를 사용하기 위해 선언해야 한다. /* 예 6.15 동일한 프로그램(모듈)에서의 외부 변수 (2) */ double x = 1.0, y = 2.0, z; /* 전역 변수 */ int main(void) { double f(); z = f(); printf(“%.1f %.1f %.1f\n”, x, y, z); /* */ } double f() double y, z; /* 지역 변수 y와 z의 선언 */ /* 지역 변수 y와 z는 숨겨짐 */ x = y = z = 3.0; /* 전역 변수 x의 값도 변경됨 */ return (x + y + z); /* 9.0이 반환됨 */

33 6.5 기억 클래스(storage class) (예 6.16) 서로 다른 프로그램에서의 외부(external) 변수
모듈A에서 정의된 int a를 모듈B에서 사용하려면 extern int a로 선언되어야 한다. 모듈A(file1.c)와 모듈B(file2.c)는 분리 컴파일 후 링크하여 다음같이 동작시킬 수 있다(UNIX기계의 경우). cc –o prog6-16 file1.c file2.c prog6-16

34 6.5 기억 클래스(storage class) (예 6.16 계속) 서로 다른 프로그램에서의 외부(external) 변수
/* 예 6.16 모듈 A(file1.c) */ int a = 0; /* 정의 */ int main(void) { sub1(); sub2(); printf(“a=%d\n”, a); } /* 예 6.16 모듈 B(file2.c) */ extern int a; /* 선언 */ sub1() { a = 5; } sub2() a = 10; (예 6.16) 분리 컴파일의 두 모듈 output a=10

35 6.5 기억 클래스(storage class) 다른 모듈에서 정의된 변수를 함수에서만 사용
전 예에서는 a가 file 앞에서 extern으로 선언되어 sub1과 sub2 모두에서 접근 가능하였다. sub2에서만 접근 가능하게 하려면 모듈B의 sub2함수 내에서 extern으로 선언하면 됨 이경우 sub1에서는 외부 전역변수 a를 사용할 수 없다. /* 예 6.16 모듈 B의 수정 */ sub1() { . . . } sub2() extern int a; /* 선언 */ a = 10;

36 6.5 기억 클래스(storage class) 기억 클래스 register(레지스터)
해당 변수를 주 메모리(main memory)가 아닌 register에 저장하라는 의미로 통상 자주 사용하는 변수에 적용한다. 기계의 레지스터 수가 부족할 수도 있으므로 보장되지는 않음 register에 저장하면 계산 속도가 빨라진다. 정상적인 경우 register의 사용 여부가 프로그램의 결과에 영향을 미치지 않는다. (예 6.17) register 변수 선언 (1) 인수 s,u와 지역변수 counter를 register 변수로 선언 /* 예 6.17 register 변수 선언(1) */ func(s, u) register int s; register char u; { float temp; register int counter; . . . }

37 6.5 기억 클래스(storage class) 기억 클래스 register(레지스터)
register int ri; int *cp; cp = &ri; /* 불가능한 구문 */ (예 6.18) register 변수 선언(2) 반복문의 인덱스 등이 레지스터 변수로 자주 사용됨 블록을 벗어나면 할당된 레지스터는 해제됨 #define LIMIT 1000 . . . { register int i; for (i = 0; i < LIMIT; i++) { }

38 6.6 수학 함수 C 언어에 내장된 수학 함수는 없다. 원시 프로그램 앞에 <math.h>란 헤더file을 포함하고, 링크시 수학 라이브러리(lm)를 연결해야 한다. cc –lm prog.c sqrt() exp() log() sin() cos() tan()등 수학 함수는 double형을 인수로 받고 double형의 값을 반환한다. 따라서 sqrt(4.0)은 올바르나 sqrt(4)는 에러 발생 가능 다음에서 x에는 2.0이 반환되어 저장됨 double x; x = sqrt(4.0);

39 6.6 수학 함수 prog6-19.c 1 /* File : prog6-19.c 2 Trigonometric tables */
6.6 수학 함수 /* File : prog6-19.c Trigonometric tables */ #include <stdio.h> #include <math.h> int main(void) { int i; double x, h, sin(), cos(), tan(); 9 printf(“\n시작값과 변위를 입력하세요(예 ): ”); scanf(“%lf%lf”, &x, &h); /* (1) */ printf(“\n\n x sin(x) cos(x) tan(x)\n”); for (i = 0; i < 5; i++) { printf(“%10g %10g %10g %10g\n”, /* (2) */ x, sin(x), cos(x), tan(x)); x = x + h; } 18 } prog6-19.c

40 6.6 수학 함수 [해 설] output double형(즉 long float형)의 자료 x와 h를 읽어 들인다.
6.6 수학 함수 [해 설] output double형(즉 long float형)의 자료 x와 h를 읽어 들인다. %g는 %f와 %e(과학적 표기) 중 짧게 표현되는 것으로 출력. %10g는 10자리를 잡고 우측정렬로 %g로 출력하라는 뜻 시작값과 변위를 입력하세요(예 ): x sin(x) cos(x) tan(x)

41 6.7 재귀 함수 재귀 함수(recursive function)는 반환값을 알고있는 인수(가령 0 등의 작은 값)를 받으면 그 값을 직접 반환한다. 그 외의 인수를 받으면 그보다 (가령 1) 작거나 간단한 인수로 자신과 동일한 이름의 함수를 호출하여 그 결과를 이용한다. (예 6.20) 재귀함수 이용하여 1부터 n까지의 합 구하기 /* File : prog6-20.c 재귀함수 이용 1부터 n까지 더하기 */ #include <stdio.h> 4 int sum(int n) { if (n <= 0) return 0; else return (n + sum(n-1)); 11 } prog6-20.c

42 6.7 재귀 함수 prog6-20.c (계 속) output 함수 호출 반환되는 값 sum(0) sum(1) sum(2)
6.7 재귀 함수 12 int main(void) 13 { int i = 5, j; j = sum(i); printf(“1에서 %d까지의 합 = %d\n”, i, j); 17 } prog6-20.c (계 속) output 1에서 5까지의 합 = 15 함수 호출 반환되는 값 sum(0) sum(1) sum(2) sum(3) sum(4) sum(5)   0   1+sum(0) 즉 1   2+sum(1) 즉 3   3+sum(2) 즉 6   4+sum(3) 즉 10   5+sum(4) 즉 15

43 prog6-21.c (반복식 factorial)
6.7 재귀 함수 Factorial 의 반복식 정의 n! = (n is 0 or 1) n! = n * (n-1) * (n-2) * 2 * 1 (n > 1) /* 예6.21 (1) 반복문 사용 factorial 계산하는 함수 */ int factorial(int n) /* 반복문 사용 */ { int product; for (product = 1; n > 1; n--) product *= n; return product; } prog6-21.c (반복식 factorial)

44 prog6-21b.c (재귀식 factorial)
6.7 재귀 함수 Factorial 의 재귀식 정의 n! = (n is 0 or 1) n! = n * (n-1)! (n > 1) /* 예6.21 (2) 재귀적으로 factorial 계산하는 함수 */ int factorial(int n) /* 재귀 호출 사용 */ { if (n <= 1) return 1; else return (n * factorial(n- 1)); } prog6-21b.c (재귀식 factorial)

45 6.8 함수를 매개변수로 사용하기 함수 자체를 다른 함수에 전달하는 방법을 제공
6.8 함수를 매개변수로 사용하기 함수 자체를 다른 함수에 전달하는 방법을 제공 가령 임의 함수f를 인수로 받아 f(m)+f(m+1) f(n)을 구하는 함수를 작성하고, 인수 f로는 sin이거나 log함수거나 사용자 정의 함수 등을 사용한다. (예 6.22) sumoff함수는 임의의 함수(f)에 대해 f(m)+f(m+1)+…f(n)을 구하는 함수이다. 인수로 square와 cube라는 함수를 사용하여 동작 prog6-22.c /* File : prog6-22.c 함수를 인수로 전달 */ #include <stdio.h> int main(void) { double sumoff(), square(), cube(); printf(“%.3f %.3f\n”, sumoff(square, 2, 5), sumoff(cube, 2, 5)); }

46 6.8 함수를 매개변수로 사용하기 - 보조함수 square와 cube prog6-22.c (계속) output
6.8 함수를 매개변수로 사용하기 /* f(m) f(n)을 구하는 함수 */ 10 double sumoff(double f(double), int m, int n) 11 { int i; double sum = 0.0; for (i = m; i <= n ; i++) sum += f((double)i); return sum; 17 } prog6-22.c (계속) output - 보조함수 square와 cube 18 double square(double x) 19 { return x * x; 21 } 22 double cube(double x) 23 { return x * x * x; 25 }  

47 6.8 함수를 매개변수로 사용하기 - 함수를 인수(즉 매개 변수)로 사용하는 법을 보았다.
6.8 함수를 매개변수로 사용하기 - 함수를 인수(즉 매개 변수)로 사용하는 법을 보았다. - 함수의 포인터를 인수로 이용하여 함수 sumoff를 재작성해보자. double (*f) ( ) 는 double형 자료 반환함수에 대한 포인터이다. 만약 double *f ( ) 로 작성하면 double형 자료의 포인터를 반환하는 함수가 되므로 조심해야 한다. - 두가지 구현법 모두 호출 방식은 동일하다. 함수를 인수로 넘겨도, 결국 함수의 주소를 이용하기 때문이다. /* f(m) f(n)을 구하는 함수 : 함수의 포인터 인수 사용 */ 10 double sumoff(double (*f)(double), int m, int n) 11 { int i; double sum = 0.0; for (i = m; i <= n ; i++) sum += (*f)((double)i); return sum; 17 }

48 6.8 함수를 매개변수로 사용하기 (예 6.23) 함수를 선택하여 호출
6.8 함수를 매개변수로 사용하기 (예 6.23) 함수를 선택하여 호출 func0 ~ func3를 prog6-23sub.c에 저장한 경우 동작 cc –o prog6-23a prog6-23a.c prog6-23sub.c prog6-23a /* File : prog6-23a.c 함수를 선택하여 호출 */ #include <stdio.h> void sub(int num) { extern func0(), func1(), func2(), func3(); switch (num) { case 0: func0(); break: case 1: func1(); break: case 2: func2(); break: case 3: func3(); break: default: printf(“FUNCTION NUMBER ERROR\n”); } 14 } prog6-23a.c

49 prog6-23a.c (계 속) 보조file prog6-23sub.c
6.8 함수를 매개변수로 사용하기 (예 6.23 계속) 함수를 선택하여 호출 15 int main(void) 16 { sub(2); 18 } prog6-23a.c (계 속) 보조file prog6-23sub.c /* File : prog6-23sub.c prog6-23a를 위한 보조 함수들 */ func0() { printf(“func0 entered\n”); } func1() { printf(“func1 entered\n”); } func2() { printf(“func2 entered\n”); 11 } 12 func3() { printf(“func3 entered\n”); 14 } func2 entered

50 6.8 함수를 매개변수로 사용하기 (예 6.23 계속) 함수명을 변수에 저장하여 사용
6.8 함수를 매개변수로 사용하기 (예 6.23 계속) 함수명을 변수에 저장하여 사용 함수의 포인터들을 저장하는 배열(a)을 이용하도록 함수 sub를 재작성 /* File : prog6-23b.c 함수명을 변수에 저장하여 사용 */ #include <stdio.h> void sub(int num) { extern func0(), func1(), func2(), func3(); static int (*a[])() = {func0, func1, func2, func3}; if (num < 0 || num > 3) printf(“FUNCTION NUMBER ERROR\n”); else (*a[num])(); 12 } 13 14 prog6-23b.c 함수 sub부분

51 6.9 함수 main()의 매개 변수 지금까지는 함수 main의 매개 변수는 사용하지 않았다.
즉 매개변수 자리에 void로 명시하였다. (예 6.24) 매개 변수를 사용하는 함수 main의 일반 형태 argc는 프로그램 동작 시 사용하는 (프로그램명을 포함하는) 인수의 수 argv는 문자열로 된 인수의 배열 int main(int argc, char *argv[]) { . . . } 또는 int main(int argc, char **argv)

52 6.9 함수 main()의 매개 변수 UNIX 명령어 echo는 그 뒤의 인수들을 표준출력으로 출력하는 간단한 명령이다(UNIX 쉘 프롬프트를 $라 가정). $ echo computer science computer science (예 6.25) echo처럼 동작하는 프로그램 prog /* File : prog.c UNIX echo와 같은 프로그램 prog작성 */ #include <stdio.h> int main(int argc, char *argv[]) { int i; for (i = 1 ; i < argc; i++) printf(“%s%c”, argv[i], (i<argc-1) ? ‘ ’ : ‘\n’); } (예 6.25) prog.c output $ cc – o prog prog.c $ prog computer science computer science

53 6.9 함수 main()의 매개 변수 (예 6.25 계속) echo처럼 동작하는 프로그램 prog
argc에는 인수의 수 즉 3이 저장되고, argv[0] 는 “prog”를 argv[1]은 “computer”를, argv[2]는 “science”를 가리킴

54 6.10 void 형 지정 함수 void형 함수에는 return 문이 없다.
void형 함수 prn_char가 다음같이 정의되면, 호출은 prn_char(‘w’);같은 식으로 한다. 만약 k = prn_char(‘w’)로 하면 에러임 void prn_char(char c) { printf(“%c has value %d”, c, c); } int main(void) { prn_char(‘w’); /* void 함수의 호출 */ } - printf문의 첫 인수 내에서 %c는 문자로 출력, %d는 정수로 출력. - 문자를 정수로 출력하면 아스키 코드값이 출력된다. w has value 119

55 전처리기(preprocessor) #include나 #define등의 전처리 기능을 전처리기가 미리 처리한 후 컴파일러에게 처리된 코드를 넘긴다. #define의 형식 매크로를 정의할 때 사용한다. (2)번 형식의 경우 매크로명과 다음 괄호사이에 공란이 없어야 한다. 공란이 있으면 (1)번 형식으로 인식함 (1) #define 매크로명 문자열_또는_ 상수 (2) #define 매크로명(매개 변수, …) 문자열

56 6.11 전처리기(preprocessor) (예 6.27) #define의 사용 TEXT array[SIZE];
/* (1) */ #define TEXT unsigned char #define SIZE 80 /* (2) */ #define max(x, y) (((x) > (y)) ? (x) : (y)) TEXT array[SIZE]; 가 다음 같이 치환됨. unsigned char array[80]; 단 토큰단위로 변환이 일어나므로 SIZE0가 800으로 치환되지는 않음. e = max(a+b, c+d); 는 다음 같이 치환됨. e = (((a+b) > (c+d)) ? (a+b) : (c+d)); (우등생 문제) max의 매크로 정의 시 과도하게 ( )가 많은 이유는?

57 6.11 전처리기(preprocessor) 흔히 쓰는 #define의 사용 예
#define PI /* π 의 값 */ #define E /* 자연 로그에 대한 밑수(base) */ #define C /* 빛의 속도(km/sec) */ #define EOF (-1) /* file의 끝 */ #define TRUE 1 /* 참 */ #define FALSE 0 /* 거짓 */ #define MAXINT /* 4바이트 정수의 최대값 */

58 6.11 전처리기(preprocessor) #define을 사용한 프로그램 변형 예
ALGOL 언어의 while do 문에 익숙한 사람은 다음같이 코딩할 수도 있다. 즉 다음의 두 구문은 같다. #define EQ == #define do /* 공 백 */ while (i EQ 1) do { . . . } while (i == 1) { . . . }

59 6.11 전처리기(preprocessor) #define에 의해 동일한 매크로가 정의되면 스택에 쌓인다.
#undef 매크로명 #define에 의해 동일한 매크로가 정의되면 스택에 쌓인다. #undef는 해당 매크로를 스택에서 pop하여 버린다. 다음의 두 구문은 같다. #define SIZE 80 a = SIZE; #define SIZE 100 b = SIZE; #undef SIZE c = SIZE; a = 80; b = 100; c = 80;

60 6.11 전처리기(preprocessor) 헤더 파일 등을 원시 프로그램 코드에 그대로 삽입하고자 하는 위치에 사용한다.
(1) #include “파일명” (2) #include <파일명> 헤더 파일 등을 원시 프로그램 코드에 그대로 삽입하고자 하는 위치에 사용한다. (1) 형은 개인적인 file을 포함시킬 때 사용한다. 현재의 디렉토리에서 해당 file을 찾고, 없으면 시스템 디렉토리에서 찾는다. (2) 형은 정해진 C 언어용 시스템 디렉토리(UNIX경우 통상 /usr/include)의 file을 포함시킬 때 사용한다.

61 6.11 전처리기(preprocessor) (예 6.29) (1) #include의 사용 file1의 내용 prog.c의 내용
#define SIZE1 80 #define SIZE2 100 prog.c의 내용 #include “file1” a = SIZE1; b = SIZE2; prog.c의 include 처리 후의 모습 #define SIZE1 80 #define SIZE2 100 a = SIZE1; b = SIZE2; define까지 처리한 후의 모습 a = 80; b = 100;

62 6.11 전처리기(preprocessor) (예 6.29) (2) #include <math.h>의 사용
<math.h>는 표준 라이브러리로서 수학함수 sin(), cos() 등을 보유하고 있다. #include <math.h> int main(void) { double val = -1.0; do { printf(“cosine of %f is %f\n”, val, cos(val)); val += 0.1; } while (val <= 1.0 ); }

63 6.11 전처리기(preprocessor) 위에서 #else와 문장들2는 생략될 수 있다.
#if 상수_수식 문장들1 #else 문장들2 #endif 위에서 #else와 문장들2는 생략될 수 있다. 상수_수식의 평가 값이 0이 아닌 경우(참일 때) 문장들1을 컴파일하고, 0일 때(거짓일 때) 문장들2를 컴파일한다.

64 6.11 전처리기(preprocessor) (예 6.30)(1) #if, #else, #endif의 사용
다음 프로그램의 경우 printf문장이 수행된다. #define MAX 100 int main(void) { #if MAX > 99 printf(“compiled for array greater than 99\n”); #endif } output compiled for array greater than 99

65 6.11 전처리기(preprocessor) (예 6.30)(2) #if, #else, #endif의 사용
다음 프로그램을 실행하면 compiled for small array가 출력된다. #define MAX 10 int main(void) { #if MAX > 99 printf(“compiled for array greater than 99\n”); #else printf(“compiled for small array\n”); #endif } output compiled for small array

66 6.11 전처리기(preprocessor) UNIX에서 컴파일시 매크로 사용하는 법
가령 #define MAX 10을 생략하고 다음 같이 프로그램을 작성할 수 있다. 이 경우 컴파일 시 –D 플래그로 매크로를 제공함. 다음 두가지 컴파일에 따라 prog의 동작 결과가 달라짐 cc –D MAX=100 prog.c –o prog cc –D MAX=10 prog.c -o prog int main(void) { #if MAX > 99 printf(“compiled for array greater than 99\n”); #else printf(“compiled for small array\n”); #endif }

67 6.11 전처리기(preprocessor) 위에서 #else와 문장들n+1은 생략될 수 있다.
#if 수식1 문장들1 #elif 수식2 문장들2 . . . #elif 수식n 문장들n #else 문장들n+1 #endif 위에서 #else와 문장들n+1은 생략될 수 있다. elif는 else if의 의미이다.

68 6.11 전처리기(preprocessor) (예 6.31) #if, #elif, #endif의 사용
화폐 단위를 선택하여 컴파일하는 경우 #define US 0 #define ENGLAND 1 #define EUROPE 2 #define ACTIVE_COUNTRY US #if ACTIVE_COUNTRY == US char currency[] = “dollar”; #elif ACTIVE_COUNTRY == ENGLAND char currency[] = “pound”; #else char currency[] = “euro”; #endif

69 6.11 전처리기(preprocessor) (예 6.32) #if의 내포 #if는 내포가 가능하다.
#if MAX > 100 #if SERIAL_VERSION int port = 198; #else int port = 200; #endif char out_buffer[100];

70 6.11 전처리기(preprocessor) 매크로명이 #define으로 이미 정의되어 있다면 문장들이 수행된다.
#ifdef 매크로명 문장들 #endif 매크로명이 #define으로 이미 정의되어 있다면 문장들이 수행된다. 필요시 #else문과도 결합될 수 있다. (예 6.33) #ifdef의 사용 color = RED;가 컴파일 된다. #define MAN 1 #undef MAN #ifdef MAN color = BLUE; #else color = RED; #endif

71 6.11 전처리기(preprocessor) 매크로명이 정의되어 있지 않다면 문장들이 수행된다.
#ifndef 매크로명 문장들 #endif 매크로명이 정의되어 있지 않다면 문장들이 수행된다. 필요시 #else문과도 결합될 수 있다. (예 6.34) #ifndef의 사용 #define TED 10 int main(void) { #ifdef TED printf(“Hi Ted\n”); #else printf(“Hi anyone\n”); #endif #ifndef RALPH printf(“RALPH not defined\n”); } #ifndef의 사용 output Hi Ted RALPH not defined

72 예 제 /* File : ex6-1.c JAVA를 출력하는 함수와 PROGRAMMING을 출력하는 함수를 이용하여 JAVA PROGRAMMING이 출력되게 하시오. */ #include <stdio.h> int main(void) { void sub1(), sub2(); /* 프로토타입 선언 */ 8 sub1(); printf(“ ”); sub2(); printf(“\n”); 11 } 12 void sub1(void) 13 { printf(“JAVA”); 15 } 16 void sub2(void) 17 { printf(“PROGRAMMING”); 19 } 예제 [해 설] output - 줄 7의 프로토타입 선언은 아직 정의되지 않은 함수를 사용하기위해 필요. 반환값이 int라면 프로토타입을 생략할 수 있다. JAVA PROGRAMMING

73 sub1()과 sub2()를 먼저 정의한 버전 프로토타입 선언을 생략할 수 있다.
JAVA를 출력하는 함수와 PROGRAMMING을 출력하는 함수를 이용하여 JAVA PROGRAMMING이 출력되게 하시오. */ #include <stdio.h> void sub1(void) { printf(“JAVA”); } void sub2(void) 10 { printf(“PROGRAMMING”); 12 } 13 int main(void) 14 { /* sub1과 sub2()가 먼저 정의되었으므로 프로토타입 선언이 필요 없다. */ sub1(); printf(“ ”); sub2(); printf(“\n”); 18 }

74 예제 6.2 1 /* File : ex6-2.c 2 실수 a, b, c를 인수로 받아 b*b – 4*a*c를
반환하는 함수를 작성하고 테스트하시오. */ #include <stdio.h> /* main 함수 */ int main(void) { double x, y, z, d, det(); printf(“%Enter 3 numbers : ”); /* long float 즉 double로 입력 */ scanf(“%lf%lf%lf”, &x, &y, &z); d = det(x, y, z); printf(“Determinant = %.3f\n”, d); 13 } 14 double det(a, b, c) 15 double a, b, c; 16 { double res; res = b*b - 4*a*c; return res; 20 } 예제 6.2

75 줄 8에서 프로토타입 double det();는 det()함수가 아직 정의되지 않았으므로 필요
예제 6.2(계 속) [해 설] output 줄 8에서 프로토타입 double det();는 det()함수가 아직 정의되지 않았으므로 필요 줄 14와 15는 함수의 인수를 표현하는 한가지 방법으로 다음과 동일하다. double det(double a, double b, double c) Enter 3 numbers : Determinant =

76 예제 6.3 output 1 /* File : ex6-3.c 2 정수를 받아 그 정수와 탭 문자를 출력하는 함수의 테스트 */
정수를 받아 그 정수와 탭 문자를 출력하는 함수의 테스트 */ #include <stdio.h> int main(void) { int a, b, c, d; void print(); a = 1234; b = a * 10; c = a / 10; d = a % 10; print(a); print(b); print(c); print(d); printf(“\n”); 13 } 14 /* print */ 15 void print(int x) 16 { printf(“%d\t”, x); 18 } 예제 output

77 /* File : ex6-4.c 쭈어진 문자를 원하는 횟수만큼 연속으로 출력하는 함수 테스트 */ #include <stdio.h> int main(void) { void repeatchars(); repeatchars(‘*’, 13); printf(“\n%s\n%7s\n%s\n”); “C Programming”, “in”, “21st century”); repeatchars(‘-’, 13); printf(“\n”); 12 } 13 14 void repeatchars(int c, int cnt) 15 { int i; for (i = 0, i < cnt; i++) putchar(c); 19 } 예제 output ************* C Programming in 21st century

78 예제 6.5(1) output 1 /* File : ex6-5.c 2 3 개의 정수 중 가장 큰 값을 반환하는 함수 */
개의 정수 중 가장 큰 값을 반환하는 함수 */ #include <stdio.h> int maxof3(int a, int b, int c) { if (a > b) if (a > c) return a; else return c; else if (b > c) return b; else return c; 15 } 예제 6.5(1) output 16 int main(void) 17 { int a = 80, b = 95, c = 92; printf(“The max of %d, %d, and %d is %d\n”, a, b, c, maxof3(a, b, c)); 21 } The max of 80, 95, and 92 is 95

79 다음같이 한 줄 짜리 프로그램으로 작성할 수도 있다. 예제 6.5 (2)
(2) maxof3함수의 재작성 다음같이 한 줄 짜리 프로그램으로 작성할 수도 있다. 예제 6.5 (2) int maxof3(int a, int b, int c) { return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); }

80 (예제 6.6) 다음 하천의 폭(x)을 구하시오. 예제 6.6

81 tan에서 반환되는 값은 double형이다.
/* File : ex6-6.c 교재 그림의 하천의 폭을 구하시오. */ #include <stdio.h> #include <math.h> int main(void) { double x, deg, len, pi; pi = ; len = 20, deg = 25; x = len * tan(deg/180*pi); printf(“%.3f meters\n”, x); 12 } 예제 6.6(계속) [해 설] output 라디안은 도/180* 이다. tan에서 반환되는 값은 double형이다. tan등의 수학 함수를 쓰려면 헤더file <math.h>를 포함시켜야 함. 컴파일/링크 시에 수학 라이브러리(lm)를 호출하여야 한다. $ cc –lm –o ex6-6 ex6-6.c $ ex6-6 9.326 meters

82 예제 6-7 1 /* File : ex6-7.c 2 주어진 값 a의 제곱근을 다음의 뉴톤-랩슨 반식으로 구하시오.
x(i) <-- (x(i-1) + a/x(i-1)) / 2 단, x의 초기값은 a를 사용하고, x값이 e-5보다 작게 변할 때까지 구하시오. */ #include <stdio.h> #include <math.h> double my_sqrt(double a) { double x1, x2, err, delta=1e-5; if (a <= 0) return (0); x1 = a; err = 0.5; while (err > delta) { x2 = (x1 + a / x1) / 2; err = x2 – x1; if (err < 0) err = -err; x1 = x2; } return x2; 19 } 예제 6-7

83 sqrt함수는 double형 인수를 받으므로 n의 값을 (double) n으로 변환(casting)하였다.
20 /* main */ 21 int main(void) 22 { int n; double r, my_sqrt(); 25 printf(“정수를 넣으시오 : ”); scanf(“%d”, &n); r = my_sqrt((double) n); printf(“%d의 제곱근 = %f\n”, n, r); 30 } 예제 6-7(계 속) [해 설] output sqrt함수는 double형 인수를 받으므로 n의 값을 (double) n으로 변환(casting)하였다. 줄24에서 프로토타입 double sqrt()는 생략할 수 있다. 함수 sqrt()가 main()보다 전에 선언되었으니까 $ cc –lm –o ex6-7 ex6-7.c $ ex6-7 정수를 넣으시오 : 2 2의 제곱근 =

84 예제 6.8 output 1 /* File : ex6-8.c 2 표준 입력을 표준 출력으로 복사하는 프로그램을 작성하시오.
표준 입력을 표준 출력으로 복사하는 프로그램을 작성하시오. 단 toupper함수를 이용하여 소문자는 대문자로 바꾸시오. */ #include <stdio.h> int main(void) { int c; while ((c = getchar()) != EOF) { c = toupper(c); putchar(c); } 12 } 예제 output $ ex6-8 < data THIS IS A BOOK OF C PROGRAMMING. HAPPY BIRTHDAY TO YOU!

85 getchar는 표준입력에서 한 문자를 읽어들이고,
putchar는 표준출력으로 한 문자를 출력. getchar의 반환값 및 putchar의 인수는 int임에 주의하시오. 표준입력에 끝에 도달하면 EOF를 반환한다. EOF는 통상 –1이 사용된다. getchar가 char아닌 int를 반환하므로 문자가 아닌 EOF를 반환할 수 있다. toupper함수는 소문자는 대문자로 바꾸고, 기타의 문자는 그대로 둔다. data란 자료file의 내용이 다음과 같다고 하자. This is a book of C programming. Happy birthday to you! 예제 6.8 (계속) [해 설]

86 블록 내에 정의된 변수는 그 블록 내에서만 유효하다. 변수 사용 시 안쪽 블록부터 그 변수가 정의되었나를 확인한다.
/* File : ex6-9.c 다음 프로그램의 실행 결과를 설명하시오. */ #include <stdio.h> int i = 0; /* 전역 변수 */ int main(void) { auto int i = 1; printf(“i=%d\n”, i); { int i = 2; printf(“i=%d\n”, i); { i += 1; printf(“i=%d\n”, i); } printf(“i=%d\n”, i); } printf(“i=%d\n”, i); 18 } 예제 [해 설] output {과 }로 둘러싸인 중문은 새로운 블록이 된다. 블록 내에 정의된 변수는 그 블록 내에서만 유효하다. 변수 사용 시 안쪽 블록부터 그 변수가 정의되었나를 확인한다. i=1 i=2 i=3

87 좌측 비트부터 출력하기 위해 bits-1-i개(즉 31-i개)의 비트를
/* File : ex6-10.c 정수를 받아 비트별로 출력하는 함수를 만들고 테스트 */ #include <stdio.h> void printbits(int val) { const int bits = 32; int i; for (i = 0; i < bits; i++) printf(“%1d”, (val >> (bits—1-i) & 1)); printf(“\n”); 11 } 12 13 int main(void) 14 { int val = 26; 16 printbits(val); 18 } 예제 [해 설] output 좌측 비트부터 출력하기 위해 bits-1-i개(즉 31-i개)의 비트를 우로 이동한 후 비트별 AND 연산자 &를 이용 최 우측 비트를 추출하여 출력 i를 0에서 31까지 (즉 31-i를 31에서 0까지) 1씩 변경시켜가며 한 비트씩 출력

88 /* File : ex6-11.c 에서 99까지의 임의 수를 생성하는 다음 rand 함수를 설명하시오. 만약 static을 제거하면 어떤 일이 벌어지는가? */ #include <stdio.h> int main(void) { int i = 0, sum = 0; for (i = 0; i < 3; i++) printf(“%d ”, rand()); printf(“\n”); 11 } 12 13 int rand() 14 { static long seed = 12345, n; seed = (seed ) % 7415; n = (seed % 1000) / 10; return n; 19 } 예제 output

89 난수(ranom number)를 구하는 프로그램으로
변경된 seed값이 함수 속에 보존되어 있도록 하기 위해 static 선언이 필요하다. static이 생략되면, auto변수로 취급되어 호출 시마다, 같은 값(81)이 반환된다. 25543 등의 숫자는 충분히 큰 임의의 수로 변경하기 위해 정한 값. 줄17에서는 가령 seed가 5812이었다면, 2째와 3째 자리수 81을 n에 저장한다. 예제 6.11(계속) [해 설]

90 예제 6.12 (분리 컴파일) file a.c file b.c
int i; int main(void) { int i, j; i = reset(); for (j = 0; j < 3; j++) printf(“i=%d; inc_i()=%d; new(i)=%d\n”, i, inc_i(), new(i)); 10 } 예제 6.12 (분리 컴파일) file a.c file b.c /* File : b.c 예제 6.12 용 */ static int i = 10; int inc_i() { i++; return i; } int new(int i) { static int val = 100; val += i; return val; 13 }

91 예제 6.12(계속) file c.c [해 설] output
extern int i; reset() { return(i); } 예제 6.12(계속) file c.c [해 설] output 파일2(b.c)의 정적 변수 i는 그 파일 내에서만 접근 가능하다. 파일3(c.c)의 reset()에서의 외부변수 i는 파일1(a.c)에서 정의된 값(1)을 반환한다. 파일2(b.c)의 함수 new내의 정적 변수 val은 새로운 함수 호출 시 전번 값을 유지한다. 파일1(a.c)에서 new(i) 호출시 사용하는 i의 값은 reset()에서 반환된 1 이다. $ cc a.c b.c c.c –o ex6-12 $ ex6-12 i=1; inc_i()=11; new(i)=101 i=1; inc_i()=12; new(i)=102 i=1; inc_i()=13; new(i)=103

92 (예제 6.13) 2 file로 작성된 프로그램 예 예제 6.13 (분리 컴파일) file one.c
For 예제 6.13(분리 컴파일) 용 */ extern int i; int main(void) { void next(); i++; printf(“%d\n”, i); next(); 10 } 11 int i = 3; 12 void next() 13 { void other(); i++; printf(“%d\n”, i); other(); 18 } 예제 6.13 (분리 컴파일) file one.c

93 예제 6.13(계 속) file two.c [해 설] output
For 예제 6.13 */ extern int i; void other() { i++; printf(“%d\n”, i); } 예제 6.13(계 속) file two.c [해 설] output one.c의 함수 main과 함수 next 및 two.c의 함수 other에서 각각 i++를 수행하였다. 이 i는 모두 one.c의 next함수를 정의하기 직전 선언된 i이다. extern int i; 는 다른 file이나 동일한 file 에서 전역적으로 선언된 외부 변수를 사용하기 위한 선언이다. 4 5 6

94 예제 6.14 1 /* File : ex6-14.c 2 1 부터 n 까지의 자연수 각각의 k승의 합을 구하는 함수 작성
k=5, n=3 일 때와 k=2, n=10일 때의 결과 확인 */ #include <stdio.h> long sum_of_powers(int k, int n) { int i, long sum = 0; long power(); /* power 함수의 프로토타입 */ for (i = 1; i <= n; i++) sum += power(i, k); return sum; 12 } 13 14 /*** m^n ***/ 15 long power(int m, int n) 16 { int i, long product = 1; for (i = 1; i <= n; i++) product *= m; return product; 21 } 예제 6.14

95 m의 n승을 구하는 power()함수를 구현하고, sum_of_powers함수에서 이용하였다.
22 int main(void) 23 { int k, n; printf(“%7ld\n”, sum_of_powers(5, 3)); printf(“%7ld\n”, sum_of_powers(2, 10)); 27 } 예제 6.14(계속) [해 설] output m의 n승을 구하는 power()함수를 구현하고, sum_of_powers함수에서 이용하였다. %ld는 long형 정수 출력을 위해 사용됨. 276 385

96 예제 6.15 1 /* File : ex6-15.c 2 정수의 하위 16비트 출력 */
정수의 하위 16비트 출력 */ #include <stdio.h> void print_16bits(int val) { int i, bitmask; bitmask = 0x8000; /* 마스크 */ for (i = 0; i < 16; i++) { if (val & bitmask) putchar(‘1’); else putchar(‘0’); bitmask >>= 1; } putchar(‘\n’); 16 } 17 int main(void) 18 { int i; for (i = 0; i < 8; i++) /* 0~7까지의 정수를 2진법으로 */ print_16bits(i); 22 } 예제 6.15

97 가령 오른쪽에서 5번째 비트가 0인지 1인지 알기 위한 비트 마스크는 16진 상수 0x10
특정 비트만 1을 갖는 비트 마스크를 이용 가령 오른쪽에서 5번째 비트가 0인지 1인지 알기 위한 비트 마스크는 16진 상수 0x10 0x8000은 우측에서 16번째 비트만이 1인 비트마스크이다. 예제 6.15(계 속) [해 설] output

98 (1) : 함수 mul의 return값(200)을 z에 저장
/* File : ex6-16.c (1)-(3)의 각 반환값은 어떻게 사용되는가? */ #include <stdio.h> int main(void) { int x, y, z; x = 10; y = 20; z = mul(x, y); /* (1) */ printf(“%d\n”, mul(x, y)); /* (2) */ mul(x, y); /* (3) */ 11 } 12 int mul(int a, int b) 13 { return a*b; 15 } 예제 [해 설] output (1) : 함수 mul의 return값(200)을 z에 저장 (2) : 함수 mul의 return값(200)을 printf의 인수로 사용 (3) : 함수 mul의 return값이 사용되지 않아 그대로 없어짐 200

99 /* File : ex6-17.c 임의의 함수 f와 두 개의 정수 m, n을 인수로 받아 f(m),f(m+1),…,f(n) 중 최대값과 최소값의 차이를 반환하는 함수 rangeoff의 구현 */ #include <stdio.h> int main(void) { double rangeoff(), square(), cube(); printf(“%.3f %.3f\n”, rangeoff(square, 2, 5), rangeoff(cube, 2, 5)); } 10 double rangeoff(double f(double), int m, int n) 11 { int i; double maxoff, minoff, val; maxoff = minoff = f(m); for (i = m + 1; i <= n; i++) if ((val = f((double)i)) > maxoff) /* 정수i를 double로 바꾸어 f호출 */ maxoff = val; else if (val < minoff) minoff = val; return (maxoff – minoff); 21 } 예제 6.17

100 2에서 5까지의 각 수의 제곱의 합과, 3제곱의 합을 계산한다. (double)i는 정수 i를 double형으로 바꾼다.
22 double square(double x) 23 { return x * x; 25 } 26 double cube(double x) 27 { return x * x * x; 29 } 예제 6.17(계속) [해 설] output 2에서 5까지의 각 수의 제곱의 합과, 3제곱의 합을 계산한다. (double)i는 정수 i를 double형으로 바꾼다. f((double)i)는 double형으로 바꾼 후 f함수를 호출한다.

101 예제 6.18(1) 1 /* File : ex6-18a.c 2 팩토리얼 함수를 반복형으로 작성 */
팩토리얼 함수를 반복형으로 작성 */ #include <stdio.h> int main(void) { int i; long fval; long fact(); /* 함수 fact()의 프로토타입 */ 9 for (i = 1; i <= 10; i++) { fval = fact(i); printf(“%2d factorial = %ld\n”, i, fval); /* long형 출력에 %ld */ } 14 } 15 long fact(int n) 16 { int i; long ans; ans = 1; for (i = 1; i <= n; i++) ans *= i; return ans; 23 } 예제 6.18(1)

102 long fact(int n)함수는 정수 n을 받아 1부터 n까지 곱한 값을 long형으로 반환한다.
예제 6.18(1) [해 설] output 1 factorial = 1 2 factorial = 2 3 factorial = 6 4 factorial = 24 5 factorial = 120 6 factorial = 720 7 factorial = 5040 8 factorial = 40320 9 factorial = 10 factorial =

103 팩토리얼 계산결과를 저장할 곳 fval을 준비하고 그 포인터를 2째 인수로 이용한다.
/* File : ex6-18b.c void 형 팩토리얼 함수를 반복형으로 작성 */ #include <stdio.h> int main(void) { int i; long fval; void fact(); /* void 형 함수 fact()의 프로토타입 */ 9 for (i = 1; i <= 10; i++) { fact(i, &fval); printf(“%2d factorial = %ld\n”, i, fval); /* long형 출력에 %ld */ } 14 } 15 void fact(int n, long *pfval) /* void 형 함수 사용 */ 16 { int i; 18 *pfval = 1; for (i = 1; i <= n; i++) *pfval *= i; 22 23 } 예제 6.18(2) [해 설] 팩토리얼 계산결과를 저장할 곳 fval을 준비하고 그 포인터를 2째 인수로 이용한다. 함수 fact내에서는 pfval이 가리키는 곳(*pfval)에 결과 저장함. *pfval은 사실은 main의 fval이다.

104 recursive call(재귀 호출)방식을 이용한 코딩으로 수학 정의를 그대로 이용한다.
/* File : ex6-19.c 피보나치 수열을 구하는 함수를 재귀식으로 작성 x(0) = 0, x(1) = 1, x(j) = x(j-1) + x(j-2) for j > 1 */ #include <stdio.h> int fib(int n) { if (n <= 1) return n; return (fib(n - 1) + fib(n - 2)); 10 } 11 예제 [해 설] recursive call(재귀 호출)방식을 이용한 코딩으로 수학 정의를 그대로 이용한다. fib(0)와 fib(1)의 경우는 각각 0과 1을 반환한다. n이 음수가 아니라 가정하고 있다. n이 2 이상의 정수이면 fib(n-1)과 fib(n-2)를 호출하여 그 반환 값을 합하여 반환한다. 가령 n이 3이면 fib(2)과 fib(1)을 호출하여 그 결과값을 이용한다(즉 합한다). 그때 호출된 fib(2)도 내부적으로 fib(1)과 fib(0)를 호출하여 그 결과 값을 합하여 반환하는 방식으로 동작한다. divide and conquer(분할 후 정복)법 즉 조금 작은 인수를 이용한 2개의 재귀 호출을 통해 얻은 값을 합성하는 방식이다.

105 fib(0)에서 fib(25)까지 한 줄에 4개씩 출력 줄17에서는 4개마다 줄바꿈 문자를 출력 기타의 경우는 탭 문자 출력
12 int main(void) 13 { int i; for (i = 0; i <= 25; i++) { printf(“fib(%d) = %d”, i, fib(i)); if (i%4 == 3) printf(“\n”); else printf(“\t”); } printf(“\n”) 20 } 예제 6.19(계속) [해 설] output fib(0)에서 fib(25)까지 한 줄에 4개씩 출력 줄17에서는 4개마다 줄바꿈 문자를 출력 기타의 경우는 탭 문자 출력 fib(0) = fib(1) = fib(2) = fib(3) = 2 fib(4) = fib(5) = fib(6) = fib(7) = 13 fib(8) = fib(9) = fib(10) = fib(11) = 89 fib(12) = fib(13) = fib(14) = fib(15) = 610 fib(16) = fib(17) = fib(18) = fib(19) = 4181 fib(20) = fib(21) = fib(22) = fib(23) = 28657 fib(24) = fib(25) = 75025

106 테스트할 함수는 인수들의 개수와 자료형이 일치하고 반환 값의 자료형도 일치해야 한다.
/* File : ex6-20.c int형 인수를 받아 double형 값을 반환하는 임의의 함수 f와, 정수 m, n을 받아 f(m)부터 f(n)까지의 각각의 수의 제곱의 합을 반환하는 함수를 구현하라. func(k) = k / (k+1) 과 inverse(k) = 1/k의 두 함수에 대해 테스트 */ #include <stdio.h> int main(void) { double func(), inverse(), sum_of_squares(); 9 printf(“%.3f %.3f\n”, i, fib(i)); sum_of_squares(func, 2, 5), sum_of_squares(inverse, 2, 5)); 13 } 14 double func(int x) /* 보조 함수 */ 15 { return (x / (x + 1.0)); 17 } 18 double inverse(int x) /* 보조 함수 */ 19 { return (x / (x + 1.0)); 21 } 예제 [해 설] 테스트할 함수는 인수들의 개수와 자료형이 일치하고 반환 값의 자료형도 일치해야 한다. 여기서는 이같은 함수 func와 inverse를 준비하였다.

107 이 함수는 int형 인수를 받아 double형 값을 반환하는 임의의 함수에 사용할 수 있다.
22 double sum_of_squares(double (*f) (int), int m, int n) 23 { int i; double sum = 0.0; 26 for (i = m; i <= n; i++) sum += (*f)(i) * (*f)(i); return sum; 30 } 예제 6.20(계속) output [해 설] 이 함수는 int형 인수를 받아 double형 값을 반환하는 임의의 함수에 사용할 수 있다. 예를 들어 func와 inverse함수 등이 사용 가능하다. 함수를 인수로 전달하는 2번째 방법을 사용함(교재 본문 참조) double 형을 반환하는 함수에 대한 포인터를 인수로 사용한다. (*f)(i)에서 (*f)는 f가 가리키는 함수이다. 즉 (*f)(i)는 인수 i에 대해 함수를 호출하는 장면이다.

108 쉘 명령어로 인수를 뒤집어서 출력하는 프로그램이다. 쉘명령어로 동작시 사용하는 (명령어도 포함한) 인수의 수가 argc
/* File : korea.c 명령어 인수처리 (예제 6.21) */ #include <stdio.h> int main(int argc, char *argv[]) { while (argc-- > 0) /* argc가 0보다 크면 1 줄인 후 다음을 수행 */ printf(“%s ” , argv[argc]); putchar(‘\n’); } 예제 output [해 설] $ cc –o korea korea.c $ korea joins top four four top joins korea 쉘 명령어로 인수를 뒤집어서 출력하는 프로그램이다. 쉘명령어로 동작시 사용하는 (명령어도 포함한) 인수의 수가 argc 사용 예의 경우 argc 는 4이다. 쉘명령어 인수는 순서대로 argv[0], argv[1], …, argv[argc-1] 사용 예의 경우 argv[0]는 “korea”, argv[1]은 “joins”, argv[2]는 “top”, argv[3]은 “four”를 가리킨다. 줄6과 7은 다음과 같은 의미이다. while (argc > 0) /* argc가 0보다 크면 1 줄인 후 다음을 수행 */ { argc--; printf(“%s ” , argv[argc]); }

109 예제 6.21(계속)

110 /* File : ex6-22.c 문자열을 인수로 받아 각 문자 사이에 공란을 넣어 출력하는 함수 */ #include <stdio.h> void print_onebyone(char *str) { while (*str) { /* 널 문자가 아닌 동안 다음을 하시오. */ printf(“%c ”, *str); /* str이 가리키는 문자와 공란을 출력 */ str++; /* str이 다음 문자 위치를 가리키게 한다. */ } printf(“\n”); 11 } 12 13 int main(void) 14 { print_onebyone(“Millennium”); 16 } 예제 output M i l l e n n i u m

111 함수 itob를 반복 호출하는 main 프로그램이다. 위에서 itob의 반환값은 binstr과 같다고 가정하고 있다.
/* File : ex6-23.c 정수를 32비트의 2진법으로 바꾸고 이를 문자 배열에 저장하는 함수 */ #include <stdio.h> #define NOBITS 32 int main(void) { int num; char binstr[NOBITS+1]; char *itob(); 10 while (1); /* 무한 반복한다. */ printf(“음수가 아닌 정수를 넣으시오(-1은 종료) : ”); scanf(“%d”, &num); if (num < 0) exit(0); /* 프로그램을 종료한다. */ printf(“%5d = %s\n”, num, itob(num, binstr, NOBITS)); 18 } 20 } 예제 [해 설 1] 함수 itob를 반복 호출하는 main 프로그램이다. 위에서 itob의 반환값은 binstr과 같다고 가정하고 있다. 줄 17은 다음과 같이 바꾸어도 된다. itob(num, binstr, NOBITS); printf(“%5d = %s\n”, num, binstr);

112 비트들의 배열대신 편의상 문자들의 배열을 사용한다. (1)에서는 문자열의 끝에 널문자를 추가한다.
21 char *itob(int num, char pb[], int isize) 22 { int i; pb[isize] = ‘\0’; /* (1) */ for (i = isize - 1; i >= 0; i--) { pb[i] = (1 & num) + ‘0’; /* (2) */ num >>= 1; } return pb; 30 } 예제 6.23(계 속) [해 설 2] 비트들의 배열대신 편의상 문자들의 배열을 사용한다. (1)에서는 문자열의 끝에 널문자를 추가한다. (2)에서는 현재 num의 최하위 비트를 (1 & num)으로 추출한 뒤, 그 값이 0이면 ‘0’으로, 1이면 ‘1’로 바꾸어 저장한다. 함수 itob의 인수들과 반환 값 첫 인수는 변환하려는 정수이다. 둘째 인수는 2진수 로 바뀐 후의 각 비트 값을 ‘0’또는 ‘1’로 저장할 문자 배열이다. 셋째 인수는 2째 인수로 전달될 배열의 크기인데, 통상 32를 사용하면 된다. itob의 반환 값은 편의상 결과가 저장된 문자열의 주소를 반환한다.

113 예제 6.23(계 속) output 음수가 아닌 정수를 넣으시오(-1은 종료) : 109
109 = 음수가 아닌 정수를 넣으시오(-1은 종료) : 1128 1128 = 음수가 아닌 정수를 넣으시오(-1은 종료) : -1 예제 6.23(계 속) output

114 (예제 6.24) 다음 선언들의 의미는? int (*)(void); void draw(void); char *p;
short *q; int prt(void *); 예제 6.24 선언의 의미 [해 설] (1) 인수가 없으며, int형을 반환하는 함수에 대한 포인터 선언 (2) 인수가 없고, 반환값이 없는 함수 draw의 선언 (3) 함수 prt는 int형의 자료를 반환하는 함수이며, 인수는 void 포인터 형을 갖는다. 이 뜻은 어떤 형의 포인터 인수나 취할 수 있음을 나타낸다. 즉 char포인터인 p나 short포인터인 q 등을 모두 함수 prt를 호출할 때 인수로 사용할 수 있다.

115 printf에서 %u는 부호 없는 정수(unsigned int)를 출력할 때 사용. 즉 주소 출력 등에 사용
/* File : ex6-25.c 함수의 주소와 자료의 주소를 출력해 보시오. 자료부와 프로그램이 저장되는 주소를 비교하시오. */ int main(void) 5 { int i = 10; char c = ‘a’; void dummy(); 9 printf(“i at %u, c at %u, main() at %u, and dummy() \ 11 at %u\n”, &i, &c, main, dummy); 12 void dummy() 13 { 14 } 예제 output [해 설] i at , c at , main() at 66536, and dummy at 66604 printf에서 %u는 부호 없는 정수(unsigned int)를 출력할 때 사용. 즉 주소 출력 등에 사용 변수 i와 c의 자료부는 함수 main과 dummy의 프로그램부와 별도의 위치에 저장되어 있음을 알 수 있다.

116 PR(a)의 정의에서 printf문의 #a는 a에 해당하는 텍스트 그대로를 의미한다. 즉 그 곳에 저장된 값이 아니다.
/* File : ex6-26.c #define을 이용한 매크로의 정의 및 사용 */ #include <stdio.h> #define FUDGE(k) k #define PR(a) printf(#a“ = %d\t”, (int)(a)) #define PRINT(a) PR(a); putchar(‘\n’) #define PRINT2(a, b) PR(a); PRINT(b) #define PRINT3(a, b, c) PR(a); PRINT2(b, c) #define MAX(a, b) (a < b ? b : a) 10 int main(void) 11 { int x = 2, y = 1, cel = 100; PRINT( x*FUDGE(2) ); PRINT2( cel, 9/5.*cel+32 ); PRINT3( MAX(x++, y), x, y); 14 } 예제 output [해 설] x* = 7 cel = /5.*cel+32 = 212 (x++ < y ? y : x++) = 3 x = 4 y = 1 PR(a)의 정의에서 printf문의 #a는 a에 해당하는 텍스트 그대로를 의미한다. 즉 그 곳에 저장된 값이 아니다.

117 (예제 6.27) 다음 문장들의 의미는? #ifdef CREDIT credit(); #elif DEBIT debit();
#else printerror(); #endif 예제 6.27(1) 전처리 [해 설] 식별자 CREDIT이 정의되어 있으면 함수 credit()이 호출되고, 식별자 DEBIT가 정의되어 있으면 함수 debit()가 호출된다. 둘다 정의되어 있지 않으면 printerror()가 호출된다.

118 (예제 6.27) 다음 문장들의 의미는? (2) #if DLEVEL > 5 #define SIGNAL 1
#if STACKUSE == 1 #define STACK 200 #else #define STACK 100 #endif #define SIGNAL 0 #define STACK 50 예제 6.27(2) 전처리 [해 설] DLEVEL>5가 참이면 첫번째 #define ~ #endif가 수행되고, 거짓이면 두번째 #define ~ #endif가 수행된다.


Download ppt "Chapter 6 – 함수와 프로그램 구조 Outline 6.1 함수 정의, 호출 및 선언 6.2 return문"

Similar presentations


Ads by Google