1k likes | 1.28k Views
CHAPTER 08. 함수. 함수의 필요성과 편리성을 이해한다 .. 함수의 종류를 살펴본다 . 함수를 정의하고 호출하는 방법을 배운다 . 함수에서 인수전달의 필요성과 방법을 배운다 . 난수 생성기를 이용하여 난수를 얻는 방법을 배운다 . 재귀 호출을 이용하는 재귀 함수를 작성하는 방법을 배운다. 8.0 개요 p.308. C 프로그램은 함수들의 모임 함수 ( function) 특정한 일을 수행하는 코드 블록 누군가 ( 다른 함수 ) 를 위해 대신 일을 해주는 역할
E N D
CHAPTER 08. 함수 함수의 필요성과 편리성을 이해한다.. 함수의 종류를 살펴본다. 함수를 정의하고 호출하는 방법을 배운다. 함수에서인수전달의 필요성과 방법을 배운다. 난수 생성기를 이용하여 난수를 얻는 방법을 배운다. 재귀 호출을 이용하는 재귀 함수를 작성하는 방법을 배운다.
8.0 개요 p.308 • C 프로그램은 함수들의 모임 • 함수(function) • 특정한 일을 수행하는 코드 블록 • 누군가(다른 함수)를 위해 대신 일을 해주는 역할 • 평균, 최댓값, 표준 편차와 같은 특정 값을 구하거나 • 파일 생성, 사용자 안내문 표시, 배열 내용 변경, 결과 출력과 같이 특정 일을 처리
8.1 함수의 종류와 실행 순서 p.308 • 하향식 프로그래밍(top-down programming) 방법 • 크고 복잡한 문제를해결하기 쉬운 여러 개의 작은 문제로 나누어 문제를 단순화시킨 후 각 문제를 함수로 작성하여 해결 • 크고 복잡한 문제가 아니더라도 작은 문제에도 적용 가능 • 프로그램의 길이 단축, 프로그램의 가독성(readability) 향상, 프로그램의 수정 및 확장의 용이, 코드의 재활용
8.1 함수의 종류와 실행 순서 p.309 • 함수의 개념을 쉽게 보여주는 실생활 예 함수 호출에 해당 함수에 해당 반환값 전달에 해당
8.1 함수의 종류와 실행 순서 p.310 • C프로그램에서 함수의 호출과 실행 순서 p.310
8.1 함수의 종류와 실행 순서 p.310 • 인수 전달 • [그림 8-2](b)에서 주소와 주문 음식, 개수를 얘기하지 않거나 중국성에서 음식을 다 만든 후에 배달해주지 않는다면? • 함수를 호출할 때는 함수가 일을 하는 데 필요한 최소의 정보(인수(argument))를 전달해야 함‘인수 전달’ • 호출된 함수는 자신을 호출한 함수에 결과(반환 값)를 제공해야 함 ‘반환 값을 전달한다’
함수의 종류 p.311 • 라이브러리 함수(library function) • C 언어에서 미리 정의해서 제공하는 함수 • 함수에 필요한 전처리기지시자와 헤더 파일을 정확하게 명시해야 함 예) • printf와 scanf → #include <stdio.h> • pow, sqrt → #include <math.h> • 사용자 정의 함수(user-defined function) • 프로그래머가 직접 정의해서 사용하는 함수
8.2.1 라이브러리 함수의 호출 p.311 • 형식 함수명( 인수 목록 ) 인수1, 인수2, …, 인수n 상수, 변수, 식 가능 → 최종적으로 인수의 값이 함수로 전달 함수마다 인수의 순서, 자료형, 개수가 정해져 있음
8.2.1 라이브러리 함수의 호출 p.312 『주의』 삼각 함수의 인수는 각도가 아닌 라디안 단위로 사용 • 180°는 3.141592(π)
8.2.2 라이브러리 함수의 사용 예 p.313 • 문제 • 0° ~180°까지 30° 단위마다 사인 함수의 값 출력하기 • 분석 • 사인 삼각함수의 값은 라이브러리 함수 sin을 이용 • sin 함수 • #include <math.h>가 필요 • 인수는 라디안값 180°: π= degree°: radian
8-1 0° ~ 180°까지 30° 단위로 사인 함수 값 출력하기 p.314 1 #include <stdio.h> 2 #include <math.h> 3 #define PI 3.141592 4 5 int main() 6 { 7 int degree; //각도저장 8 double radian; // degree의 라디안 값 저장 변수 9 10 for (degree=0; degree<=180; degree+=30) 11 { 12 radian = (PI * degree) / 180; // 각도 → 라디안 13 printf("sin(%d˚) = %.5lf \n", degree, sin(radian)); 14 } 15 16 return 0; 17 }
8.3 사용자 정의 함수 p.315 • 사용자 정의 함수 • 라이브러리 함수가 없을 때main 함수처럼 프로그래머가 직접 정의하는 함수 • 호출 방법 :라이브러리 함수와 동일정의 방법 :main 함수와 동일 • 인수 전달에 대한 자세한 내용은 8.6에서소개,여기서는 사용자 정의 함수를 만들어 이용하는 방법에 초점을 맞춘다.
8.3.1 사용자 정의 함수의 호출과 정의 p.315 사용자 정의 함수의 호출 • 함수명(인수 목록) • 사용자 정의 함수의 정의 매개변수 = 인수; 효과 • 반환값자료형 함수명( 매개변수 선언 목록 ) • { • 함수에서 사용할 변수 선언부 • 함수에서 처리할 명령 코드부 • return 반환 값; • } 함수 호출문 =반환 값; 효과 p.316 주의 필독
8.3.1 사용자 정의 함수의 호출과 정의 p.316 • 예) double V(int r) { double pi = 3.141592; return (4 * pi * pow(r, 3)) / 3; } • 호출문 인수 • 상수, 변수, 수식 가능 • 매개변수 • 변수만 가능(인수의 값을 저장해야 하므로) int main() { int radius = 5; printf("체적:%lf\n", V(radius)); return 0; }
8.3.2 인수와 매개변수에 대한 이해 p.320 • 용어정리 • 함수 간에 전달 정보 • 인수(argument), 인자(argument), 매개변수(parameter) 또는 파라미터 • 실(actual)/형식(formal) 구분 • ‘실(actual)’: 함수 호출 시 전달하는 정보(실제 값) • ‘형식(formal)’: 전달받은 정보 저장 • 예) 실인수/형식인수, 실매개변수/형식매개변수, 실인수/가인수 등 • 이 책의 용어 • 함수 호출 시 명시하는 정보 : ‘인수’ • 함수 정의 시 명시하는 정보 : ‘매개변수’ (전달된 정보를 저장하는 변수임을 강조하여)
8.4.1 두 수 중 큰 값 구하기 p.320 • 문제 • 입력: 임의의 두 정수 n1과 n2 • 출력: 두 정수중 큰 값 max • 사용자정의 함수: find_larger • 해결 과정 1. main 함수에서 n1과 n2의 값을 입력받기 2. main 함수에서 find_larger 함수를 호출하여 n1과 n2 중 큰 값을 반환받아 max에 저장하기 max = find_larger(n1, n2); 3. main 함수에서 max 출력하기
8-3 find_larger 함수를 이용 n1, n2 중 큰 값 구하기 p.321 1 #include <stdio.h> 2 3 // find_larger 함수 정의 4 int find_larger(int first, int second) 5 { 6 int larger; 7 8 if (first > second) 9 larger = first; 10 else 11 larger = second; 13 return larger; 14 } 16 // main 함수의 정의 17 int main() 18 { 19 int n1, n2, max; 20 21 printf("첫째 정수? "); scanf("%d", &n1); 22 printf("둘째 정수? "); scanf("%d", &n2); 23 24 // find_larger 함수 호출 25 max = find_larger(n1, n2); 26 27 printf("%d, %d 중 큰 값은 %d \n", n1, n2, max); 29 return 0; 30 } Q1) 4행(함수정의)에서는왜 매개변수를 선언해야하고25행(함수호출)에서는 인수를 그냥 사용할까? Q2) 28행에서 n1+n2와 n1*n2중 큰 값을 max에 저장하려면?
8.5 함수 원형 선언 p.322 8.5.1 함수 호출과 함수 정의의 위치 • 함수 안에 다른 함수를 정의할 수 없다. 한 함수의 정의가 끝난 후 다른 함수를 정의해야 함 • 기본적으로 함수 정의는 함수 호출 전에 위치 • 함수 정의보다 함수 호출이 앞에 있으면 잘못 • 함수 호출이 앞에 있게 하려면 함수 원형 선언이 필요 함수1 정의 함수2 정의 함수3 정의 함수1 정의 함수2 정의 : 함수1호출문; :
8.5 함수 원형 선언 p.322 • p.322 예의잘못된 점은? • 함수의 정의보다 함수 호출이 앞에 있다. → 에러 • 함수 호출문 뒤에 함수를 정의하려면? → 함수의 원형 선언이 필요 • 1 #include <stdio.h> • 3 // main 함수의 정의 • 4 int main() • 5 { • 6 int n1, n2, max; • 8 printf("첫째 정수? "); scanf("%d", &n1); • 9 printf("둘째 정수? "); scanf("%d", &n2); • 10 • 11 // find_larger 함수를 호출 후 반환된 값을 max에 저장 • 12 max = find_larger(n1, n2); • 13 • 14 printf("%d, %d 중 큰 값은 %d \n", n1, n2, max); • 16 return 0; • 17 } • 19 // find_larger 함수의 정의 • 20 int find_larger(int first, int second) • 21 { • 22 : 함수의 본체 • 23 }
8.5.2 함수 원형 선언 p.323 • 함수의 원형(prototype) • 함수 정의의 헤더 부분에 해당 • 함수의 원형 선언 • 최소한 맨 처음 나타나는 함수 호출문보다 앞에 있어야 함 • 대부분 프로그램 상단 main 함수 정의 전에 둠 • 사용자 정의 함수는 main 함수 뒤에 둠 # 전처리기 지시자 함수1의 원형 선언 함수2의 원형 선언 main함수정의 : 함수1 호출문 : 사용자 정의 함수1 : 함수2 호출문 : 사용자 정의 함수2 :
8.5.2 함수 원형 선언 p.323 • 형식 반환값자료형 함수명( 매개변수자료형목록 ) ; • 최초 함수 호출문보다 앞에 선언해야 함 • 매개변수의 이름은 생략 가능, 매개변수의 자료형은 반드시 명시 ※함수 정의 첫 줄 즉 헤더에 해당하므로 복사하여 사용하면 편리 • 예 • int find_larger(int first, int second); • int find_larger(int, int); 각각 선언해야 함 intfirst, second 는에러 꼭 필요
8-4 함수 원형 선언 예: [프로그램 8-3]의 변형 p.324 1 #include <stdio.h> 2 4 int find_larger(int first, int second); 5 7 int main() 8 { 9 int n1, n2, max; 11 printf("첫째 정수? "); scanf("%d", &n1); 12 printf("둘째 정수? "); scanf("%d", &n2); 13 15 max = find_larger(n1, n2); 16 printf("%d, %d 중 큰 값은 %d \n", n1, n2, max); 17 18 return 0; 19 } 22 int find_larger(int first, int second) 23 { 24 if (first > second) 25 return first; 26 else 27 return second; 28 }
8.6 함수의 인수 전달: 값에 의한 호출 p.327 • 함수 간 인수 전달 방법 • 값에 의한 호출 (call-by-value) • 인수의 값을 전달 • 호출된 함수에서 자신을 호출한 함수의 인수 내용을 수정할 수 없음→ 함수 간 독립성 보장 • 주소에 의한 호출 (call-by-address) • 인수의 주소를 전달 • 호출된 함수에서 자신을 호출한 함수의 인수 내용을 수정 가능→ 부작용(side-effect)에주의해야 함 • 포인터 개념을 알아야 하므로 ‘10장 포인터’에서 자세히 소개
8.6.1 값에 의한 호출과 인수 전달 과정 p.327 • 값에 의한 호출(call-by-value) p.327 → 함수를 호출하면 인수의 값이 전달됨 → 매개변수 기억장소가 확보되고 전달된 값이 매개변수에 저장됨 즉 인수와 매개변수는 서로 다른 기억장소를 사용 → 호출된 함수에서 매개변수 값을 변경하더라도 인수는 변경되지 않음 → 함수 간 독립성 보장 • 함수 호출 함수명(인수1, 인수2, …, 인수n) • 함수 정의 반환값형 함수명(자료형 매개변수1, 자료형 매개변수2, …) { : 함수 본체 } • ★ 주의 • 인수: 상수, 변수, 식 • 매개변수: 변수만 가능
8.6.1 값에 의한 호출과 인수 전달 과정 p.327 • 값에 의한 호출 예 p.327 • 함수 호출문 • pass(absence) • larger(a+100, a+10) • multiply(sum, 7) • 함수 정의 • int pass(int n) { … } • int larger(int a, int b) { … } • int multiply(double n, int times){ … }
8.6.2 값에 의한 호출과 함수 간의 독립성 p.329 값에 의한 호출과 함수 간 독립성을 보여주는 예 • 문제 • 입력: 근로자 오늘 하루 근로 시간 • 출력: 근로자의 하루 임금 • 임금: 시급제와 일급제를 혼용 • 시급: 10,000원 • 8시간 이상 근로 → 일급으로 100,000원을 적용
8.6.2 값에 의한 호출과 함수 간의 독립성 p.329 • 분석 • 일급을 적용할 근로 일: (총 근로 시간 / 8) • 시급을 적용할 나머지 근로 시간: (총 근로 시간 % 8) • 오늘의 임금 = 근로 일*일급 + 나머지 근로 시간*시급 • 임금을 계산하는 함수 get_pay
8-7 시급제와 일급제를 혼용한하루 임금 계산하기 p.330 1 #include <stdio.h> 2 3 // 함수의 원형 선언 4 int get_pay(int hours, int day_rate, int hour_rate); 5 6 // main 함수의 정의 7 int main() 8 { 9 int total_hours; // 근로 시간 10 int daily_rate = 100000, hourly_rate = 10000; // 일급, 시급 11 int pay; // 임금 12 13 printf("근로 시간은? "); 14 scanf("%d", &total_hours);
8-7 시급제와 일급제를 혼용한하루 임금 계산하기 p.330 16 pay = get_pay(total_hours, daily_rate, hourly_rate); 17 printf("총 하루 임금은 %d원 \n", pay); 18 19 return 0; 20 } 23 int get_pay(int hours, int day_rate, int hour_rate) 24 { 25 int day = hours / 8; // 근로 일 계산 26 hours = hours % 8; // 나머지 시간 계산 27 28 return (day * day_rate + hours * hour_rate); 29 } 인수 전달 결과 Q) 함수 호출 직후 즉 16행의 함수 호출이 끝난 후 total_hours의 값은? A) 다음 슬라이드
8-7 시급제와 일급제를 혼용한하루 임금 계산하기 p.330 14 scanf("%d", &total_hours); 16 pay = get_pay(total_hours, daily_rate, hourly_rate); 17 printf("총 하루 임금은 %d원 \n", pay); 18 19 return 0; 20 } 23 int get_pay(int hours, int day_rate, int hour_rate) : • 16행의 함수 호출 직전의 total_hours와 함수 실행 완료 직후의 total_hours의 값은 같다. • 이유) 값에 의한 호출 방식에서 인수 total_hours와 매개변수 hours는 다른 기억장소를 사용함 • get_pay함수에서 매개변수 hours를 수정하더라도 main 함수의 total_hours는 절대 변하지 않음 • 함수를 호출하는 함수는 자신의 변수를 마음 놓고 인수로 전달할 수 있다. • 프로그래밍에서 중요한 함수의 독립성 보장 개념
8.7 void p.332 • void • C 언어의 예약어 • 함수 정의 시 특정 자리에 명시할 내용이 없음을 나타내는 데 사용 • 함수가 반환할 값이 없다면→ 반환 형 자리에 void • 함수가 매개변수가 없다면 → 매개변수 목록에 void 또는 비워두기
8.7.1 반환 값이 없는 void 함수 p.332 • 반환 값이 있는 함수 • 특정 일을 수행한 후 반환 값을 전달하는 함수 • 실생활의 예
8.7.1 반환 값이 없는 void 함수 p.333 • 반환 값이 없는 함수 p.333 • 특정 일만 수행하고 반환 값을 전달하지 않는 함수 • void 함수라고 함 • 실생활의 예
8.7.1 반환 값이 없는 void 함수 p.333 main 함수는 반환 값이 있게도 없게도 정의 가능 • 반환 값이 있는 main 함수 • 자신을 호출한 OS에 반환 값을 전달 • return 0; 정상 종료를 의미하는 반환 값은 0 • return 1; 비정상적인 종료를 의미하는 반환 값은 0 외의 값(일반적으로 1) • 반환 값이 없는 main함수 • 반환 형 자리에 void를사용 • 반환 값이 있는 main 함수가표준
8.7.1 반환 값이 없는 void 함수 p.333 • 반환 값이 있는 main 함수와 없는 main 함수 int main() { int i, sum = 0; for (i=1; i<=10; i+=2) sum += i; printf("1~10까지의 홀수의 합:%d", sum); return 0; } void main() { int i, sum=0; for (i=1; i<=10; i++) sum += i; printf("1~10까지의 합:%d", sum); return; } • 함수 실행 끝내고 호출한 곳으로 돌아감 • 맨 마지막 문장이라면 생략 가능
8.7.1 반환 값이 없는 void 함수 p.334 • void 함수의 호출 위치 반환 값이 없으므로 식 안에 둘 수 없다. 호출문 단독으로 한 문장 5 int main() 6 { 7 print_line(2); 8 printf(" 대한대학교 한국학과 \n"); 9 print_line(2); 10 no_line = print_line(2) + printf_line(2); 11 12 return 0; 13 } 14 15 // -로 그은 선을 n행 출력 16 void print_line(int n) 17 { 18 int i; 19 for (i=1; i<=n; i++) 20 printf("---------------------------------------\n"); 21 } • 책 수정 • main 함수에 intno_line; 선언문추가
8.7.2 매개변수가 없는 함수 p.335 • 인수 전달이 필요 없는 함수 p.335 • 함수 단독으로 모든 일을 해결할 수 있다면 인수를 전달할 필요가 없다. 사용자 정의 함수의 호출 함수명 ( ) ; • 사용자 정의 함수의 정의 • 반환값자료형 함수명( void) • { • : • return 반환값; • } 생략 가능 ()
8-8 보고서 제목과 개인 정보 출력하기p.336 1 #include <stdio.h> 3 void print_title(); 4 void print_information(void); 5 6 int main() 7 { 8 print_title(); 9 print_information(); 10 11 return 0; 12 } • 14 // 보고서 제목 출력 • 15 void print_title() • 16 { • 17 printf("==========================\n"); • 18 printf("== C 프로그래밍 과제 ==\n"); • 19 printf("== 사인 함수 그래프 그리기 ==\n"); • 20 printf("==========================\n"); • 21 } • 23 //개인정보출력하기 • 24 void print_information(void) • 25 { • 26 printf("\n\n"); • 27 printf("%30s \n", "대한대학교"); • 28 printf("%30s \n", "IT학부"); • 29 printf("%30s \n", "홍길동"); • 31 }
8.10 호출된 함수가 또 다른 함수 호출하기 p.343 • 호출된 함수에서도 필요에 따라 다른 함수를 호출하여 그 결과를 이용할 수 있다. • 실생활의 예
8.10 호출된 함수가 또 다른 함수 호출하기 p.343 • 문제 • 입력: 연도(year) • 출력: 입력 연도의 총일 즉 365 또는 366 • 분석 • 함수 intleap_year(int y): y년도의 윤년 여부 반환 • y가 윤년이면 1(TRUE), 평년이면 0(FALSE)을 반환 • 윤년의 조건 • 연도가 400의 배수면 무조건 윤년또는 연도가 4의 배수지만 100의 배수가 아니면 윤년 • 함수 int days(intyy): yy년의 총일 구하기 • yy가 윤년이면 366, 평년이면 365 반환 • 핵심) • yy의 윤년 여부를 leap_year함수를 이용해 구하기
8-12 사용자가 입력한 해의 총 일 출력하기 p.345 1 #include <stdio.h> 2 #define TRUE 1 3 #define FALSE 0 4 5 // 함수의 원형 선언 6 int days(int yy); 7 int leap_year(int y); 8 9 int main() 10 { 11 int year; 12 13 printf("총 일을 구하고 싶은 연도는? "); scanf("%d", &year); 14 15 // days 함수를 호출해 year년의 총 일을 구하기 16 printf("%d년은 %d일까지 있습니다. \n", year, days(year)); 17 18 return 0; 19 }
8-12 사용자가 입력한 해의 총 일 출력하기 p.345 : 16 printf("%d년은 %d일까지 있습니다. \n", year, days(year)); : 20 21 int days(int yy) // yy년의 총 일 반환 22 { 23 if (leap_year(yy) == TRUE) // if (leap_year(yy))와 동일 24 return 366; 25 else 26 return 365; 27 } 28 29 int leap_year(int y) // y년도의 윤년 여부 반환 30 { 31 if (y%400 == 0 ‖ ((y%4==0) && (y%100 != 0))) 32 return TRUE; 33 else 34 return FALSE;; 35 }
8.9 함수의 인수 전달: 주소에 의한 호출 p.338 8.9.1 주소에 의한 호출의 필요성 • 값에 의한 호출 방식 • 함수 간 독립성을 보장하므로 많이 사용됨 • 문제: 호출된 함수에서 자신을 호출한 함수의 인수를 수정할 수 없다. • 호출된 함수에서 자신을 호출한 함수의 인수를 변경하고 싶다면주소에 의한 호출을 이용해야 한다.
8-10 값에 의한 호출로는 해결할 수 없는 문제의 예 p.339 6 int main() 7 { 8 int point1 = 5000, point2= 4000; 10 printf("함수 호출 전 포인트 점수 %d, %d \n", point1, point2); 12 sort(point1, point2); 14 printf("함수 호출 후 포인트 점수 %d, %d \n", point1, point2); 16 return 0; 17 } 20 void sort(int first, int second) 21 { 22 int temp; 24 if (first > second) 25 { 26 temp = first; 27 first = second; 28 second = temp; 29 } 31 printf("\n( sort 함수의 정렬 결과 first: %d, second: %d ) \n\n", first, second); 32 } sort 함수에서는 정렬이 되었지만 main 함수의 두 변수는 정렬되지 않았다.
8.9.1 주소에 의한 호출의 필요성p.340 • [프로그램 8-10]의 값에 의한 호출의 문제점 • sort 함수 호출 후에도 main 함수의 point1과 point2는 정렬되지 않는다. • 이유) sort 함수가 정렬하는 것은 자신의 변수 first와 second이지 main 함수의 point1과 point2가 아니기 때문 • 해결) sort 함수에서 main 함수의변수 내용을 수정할 수 있도록 주소에 의한 호출 인수 전달을 사용한다. → 10장 포인터에서 자세히 소개
8.11 배열을 함수로 전달하기 p.346 • 배열 원소 전달 • 값에 의한 호출과 주소에 의한 호출 둘 다 사용 가능 • 배열 전체 전달 • 주소에 의한 호출 방식만 사용 가능 이유) 기억장소의 낭비와 시간 낭비 제거 • 직접 포인터를 이용하는 방법은 10장에서 소개이 절에선 포인터 개념과 상관없이 배열을 함수로 전달하는 방법을 소개
8.11 배열을 함수로 전달하기 p.346 • 배열 원소 한 개 전달하기 p.346 배열 원소 :배열명[첨자] • 함수 호출 함수명( 배열명[첨자] ) • 함수 정의 반환값형 함수명( 자료형 매개변수 ) { : 함수 본체 } • 2차원 배열원소라면함수명(배열명[첨자1][첨자2]) • 배열원소의 값 1개가 전달되므로 함수측에서는 차원과 상관없이 값 1개를 저장할 변수로 선언
8-13 배열 원소 두 개 중 큰 값을 함수를 이용해 구하기 p.347 5 int main() 6 { 7 int max, score[5] = {10, 8, 9, 7, 8}; 8 9 max = find_larger(score[3], score[4]); // 4째, 5째 원소를 전달 10 11 printf("score[3]=%d과 score[4]=%d 중 큰 값은 %d \n", score[3], score[4], max); 13 return 0; 14 } 16 // 두 정수 중 큰 값을 반환하는 함수 17 int find_larger(int first, int second) 18 { 19 if (first > second) 20 return first; 21 else 22 return second; 23 }