쉽게풀어쓴 C 언어 Express 제 9 장함수와변수
이번장에서학습할내용 반복의개념이해 변수의속성 전역, 지역변수 자동변수와정적변수 재귀호출 이번장에서는함수와변수와의관계를집중적으로살펴볼것이다. 또한함수가자기자신을호출하는재귀호출에대하여살펴본다.
변수의속성 변수의속성 : 이름, 타입, 크기, 값 + 범위, 생존시간, 연결 범위 (scope) : 변수가사용가능한범위, 가시성생존시간 (lifetime) : 메모리에존재하는시간연결 (linkage) : 다른영역에있는변수와의연결상태 생존시간 범위 연결 생성 x 3 x 소멸
변수의범위 변수의범위 전역변수 지역변수 함수 함수 전역변수 지역변수 지역변수
전역변수와지역변수
지역변수 지역변수 (local variable) 는블록안에선언되는변수
지역변수선언위치 블록첫부분에서정의 int sub(void) int x; // 1... x = 100; int y; // 2 변수를함수의첫부분에선언하지않으셨군요. 컴파일오류입니다.
지역변수의범위 void sub1(void) y = 4; int y;... 지역변수는선언된블록을떠나면안됩니다.
이름이같은지역변수
지역변수의생존기간 int x; x 생성 블록 생존시간 소멸
지역변수예제 #include <stdio.h> int main(void) int i; 1212121212 temp for(i = 0;i < 5; i++) int temp = 1; printf("temp = %d\n", temp); temp++; return 0; temp = 1 temp = 1 temp = 1 temp = 1 temp = 1 블록이시작할때마다생성되어초기화된다.
지역변수의초기값 #include <stdio.h> int main(void) int temp; printf("temp = %d\n", temp); 초기화되지않았으므로쓰레기값을가진다. temp = -858993460
함수의매개변수 int inc(int counter) counter++; return counter; 매개변수도일종의지역변수
함수의매개변수 #include <stdio.h> int inc(int counter); int main(void) int i; 10 10 i 11 i = 10; printf(" 함수호출전 i=%d\n", i); inc(i); printf(" 함수호출후 i=%d\n", i); return 0; int inc(int counter) counter++; return counter; 함수호출전 i=10 함수호출후 i=10 매개변수도일종의지역변수
전역변수 전역변수 (global variable) 는함수외부에서선언되는변수이다. 전역변수의범위는소스파일전체이다. int x = 123; void sub1() x = 456; 전역변수 void sub2() x = 789;
전역변수의초기값과생존기간 #include <stdio.h> int counter; // 전역변수 void set_counter(int i) counter = i; // 직접사용가능 int main(void) printf("counter=%d\n", counter); counter = 100; // 직접사용가능 printf("counter=%d\n", counter); * 전역변수의초기값 : 0 * 생존기간 : 프로그램시작부터종료 20 i 100 200 set_counter(20); printf("counter=%d\n", counter); return 0; counter=0 counter=100 counter=20
전역변수의사용 // 전역변수를사용하여프로그램이복잡해지는경우 #include <stdio.h> void f(void); 출력은어떻게될까요? int i; int main(void) for(i = 0;i < 3; i++) f(); return 0; void f(void) for(i = 0;i < 5; i++) printf("#\n"); ( O X ) ( O ) 0123 4 56 i # # # # #
전역변수의사용 거의모든함수에서사용하는공통적인데이터는전역변수로한다. 일부의함수들만사용하는데이터는전역변수로하지말고함수의인수로전달한다.
같은이름의전역변수와지역변수 // 동일한이름의전역변수와지역변수 #include <stdio.h> int sum = 1; int main(void) int sum = 0; // 전역변수 // 지역변수 printf("sum = %d\n", sum); 지역변수가전역변수를가린다. 1 sum 0 sum < 전역변수 sum> < 지역변수 sum> return 0; sum = 0
중간점검 변수의범위는대개무엇으로결정되는가? 변수의범위에는몇가지의종류가있는가? 파일범위를가지는변수를무엇이라고하는가? 블록범위를가지는변수를무엇이라고하는가? 지역변수를블록의중간에서정의할수있는가? 똑같은이름의지역변수가서로다른함수안에정의될수있는가? 지역변수가선언된블록이종료되면지역변수는어떻게되는가? 지역변수의초기값은얼마인가? 함수의매개변수도지역변수인가? 전역변수는어디에선언되는가? 전역변수의생존기간과초기값은? 똑같은이름의전역변수와지역변수가동시에존재하면어떻게되는가?
생존기간 정적할당 (static allocation): 프로그램실행시간동안계속유지 자동할당 (automatic allocation): 블록에들어갈때생성 블록에서나올때소멸
생존기간 생존기간을결정하는요인 변수가선언된위치 저장유형지정자저장유형지정자 auto register static extern
저장유형지정자 auto 변수를선언한위치에서자동으로만들어지고블록을벗어나게되며자동으로소멸되는저장유형을지정 지역변수는 auto가생략되어도자동변수가된다. int main(void) auto int sum = 0; int i = 0;...... 0 0 sum i 전부자동변수로서함수가시작되면생성되고끝나면소멸된다.
#include <stdio.h> void sub(void); 저장유형지정자 static int main(void) int i; for(i = 0;i < 3; i++) sub(); return 0; void sub(void) int auto_count = 0; static int static_count = 0; 01 0123 auto_count static_count 자동지역변수 auto_count++; static_count++; printf("auto_count=%d\n", auto_count); printf("static_count=%d\n", static_count); 정적지역변수로써 static 을붙이면지역변수가정적변수로된다. auto_count=1 static_count=1 auto_count=1 static_count=2 auto_count=1 static_count=3
저장유형지정자 register 레지스터 (register) 에변수를저장. register int i; for(i = 0;i < 100; i++) sum += i; CPU 안의레지스터에변수가저장됨
중간점검 저장유형지정자에는어떤것들이있는가? 지역변수를정적변수로만들려면어떤지정자를붙여야하는가? 변수를 CPU 내부의레지스터에저장시키는지정자는? 컴파일러에게변수가외부에선언되어있다고알리는지정자는? static 지정자를변수앞에붙이면무엇을의미하는가?
실습 : 로그인횟수제한 로그인시에제한된횟수만큼틀리면로그인시도를막는프로그램을작성하여보자.
실행결과 id: 1111 pass: 1111 id: 1111 pass: 1111 id: 1111 pass: 1111 횟수초과
알고리즘 while(1) 사용자로부터아이디를입력받는다. 사용자로부터패스워드를입력받는다. 만약로그인시도횟수가일정한도를넘었으면프로그램을종료한다. 아이디와패스워드가일치하면로그인성공메시지를출력한다. 아이디와패스워드가일치하지않으면다시시도한다.
소스 #include <stdio.h> #include <stdlib.h> #define SUCCESS 1 #define FAIL 2 #define LIMIT 3 int check(int id, int password); int main(void) int id, password, result; while(1) printf("id: \b\b\b\b"); scanf("%d", &id); printf("pass: \b\b\b\b"); scanf("%d", &password); result = check(id, password); if( result == SUCCESS ) break; printf(" 로그인성공 \n"); return 0;
소스 int check(int id, int password) static int super_id = 1234; static int super_password = 5678; static int try_count = 0; try_count++; if( try_count >= LIMIT ) printf(" 횟수초과 \n"); exit(1); if( id == super_id && password == super_password ) return SUCCESS; else return FAIL; 정적지역변수
도전문제 위의프로그램은정적지역변수를사용한다. 정적전역변수를사용할수도있다. 정적전역변수를사용하도록위의프로그램을변경하여보자. 아이디와패스워드에대하여시도횟수를다르게하여보자. 은행입출금시스템을간단히구현하고여기에로그인기능을추가하여보자.
저장유형지정자 extern extern1.c #include <stdio.h> 컴파일러에게변수가다른곳에서선언되었음을알린다. int x; // 전역변수 extern int y; // 현재소스파일의뒷부분에선언된변수 extern int z; // 다른소스파일의변수 int main(void) extern int x; // 전역변수 x를참조한다. 없어도된다. x = 10; y = 20; z = 30; return 0; int y; // 전역변수 extern2.c int z;
연결 연결 (linkage): 다른범위에속하는변수들을서로연결하는것 외부연결 내부연결 무연결 전역변수만이연결을가질수있다.
외부연결 전역변수를 extern 을이용하여서서로연결
linkage1.c 연결예제 #include <stdio.h> int all_files; // 다른소스파일에서도사용할수있는전역변수 static int this_file; // 현재의소스파일에서만사용할수있는전역변수 extern void sub(); int main(void) 연결 sub(); printf("%d\n", all_files); return 0; linkage2.c extern int all_files; void sub(void) all_files = 10; 010 10 0
함수앞의 static main.c #include <stdio.h> extern void f2(); int main(void) f2(); return 0; sub.c static void f1() printf("f1() 이호출되었습니다.\n"); void f2() f1(); printf("f2() 가호출되었습니다.\n"); Static 이붙는함수는파일안에서만사용할수있다.
저장유형정리 일반적으로는자동저장유형사용권장 자주사용되는변수는레지스터유형 변수의값이함수호출이끝나도그값을유지하여야할필요가있다면지역정적 만약많은함수에서공유되어야하는변수라면외부참조변수 저장유형 키워드 정의되는위치 범위 생존시간 자동 auto 함수내부 지역 임시 레지스터 register 함수내부 지역 임시 정적지역 static 함수내부 지역 영구 전역 없음 함수외부 모든소스파일 영구 정적전역 static 함수외부 하나의소스파일 영구 외부참조 extern 함수외부 모든소스파일 영구
예제 : 난수발생기 자체적인난수발생기작성이전에만들어졌던난수를이용하여새로운난수를생성함을알수있다. 따라서함수호출이종료되더라도이전에만들어졌던난수를어딘가에저장하고있어야한다
main.c #include <stdio.h> unsigned random_i(void); double random_f(void); extern unsigned call_count; int main(void) register int i; 예제 // 외부참조변수 // 레지스터변수 for(i = 0; i < 10; i++) printf("%d ", random_i()); printf("\n"); for(i = 0; i < 10; i++) printf("%f ", random_f()); printf("\n 함수가호출된횟수 = %d \n", call_count); return 0;
예제 random.c // 난수발생함수 #define SEED 17 #define MULT 25173 #define INC 13849 #define MOD 65536 unsigned int call_count = 0; static unsigned seed = SEED; // 전역변수 // 정적전역변수 unsigned random_i(void) seed = (MULT*seed + INC) % MOD; call_count++; return seed; double random_f(void) seed = (MULT*seed + INC) % MOD; call_count++; return seed / (double) MOD; 48574 61999 40372 31453 39802 35227 15504 29161 14966 52039 0.885437 0.317215 0.463654 0.762497 0.546997 0.768570 0.422577 0.739731 0.455627 0.720901 함수가호출된횟수 = 20
가변매개변수 #include <stdio.h> #include <stdarg.h> int sum( int,... ); int main( void ) int answer=sum(4,4,3,2,1); printf( " 합은 %d입니다.\n", answer); return( 0); int sum( int num,... ) int answer = 0; va_list argptr; va_start( argptr, num ); for( ; num > 0; num-- ) answer += va_arg( argptr, int ); va_end( argptr ); return( answer); 합은 10 입니다.
순환 (recursion) 이란? 알고리즘이나함수가수행도중에자기자신을다시호출하여문제를해결하는기법 팩토리얼의정의 n! n 1 * ( n 1)! n n 1 2
팩토리얼구하기 팩토리얼프로그래밍 #2: (n-1)! 팩토리얼을현재작성중인함수를다시호출하여계산 ( 순환호출 ) int factorial(int n) if( n<=1)return(1); else return (n * factorial(n-1) ); 3 n 3! 은? 3! 를계산하려면 3! = 3*2! 2! 를계산하려면 2! = 2*1! 1! 은 1 3! 는? 2! 는? 1! 는? 6 2 1
팩토리얼구하기 팩토리얼의호출순서 factorial(3) = 3 * factorial(2) = 3 * 2 * factorial(1) = 3 * 2 * 1 = 3 * 2 = 6 4 3 factorial(3) if( 3 >= 1 ) return 1; else return (3 * factorial(3-1) ); factorial(2) if( 2 >= 1 ) return 1; else return (2 * factorial(2-1) ); factorial(1) if( 1 >= 1 ) return 1;... 1 2
순환알고리즘의구조 순환알고리즘은다음과같은부분들을포함한다. 순환호출을하는부분 순환호출을멈추는부분 int factorial(int n) if( n == 1 ) return 1 순환을멈추는부분 else return n * factorial(n-1); 순환호출을하는부분 만약순환호출을멈추는부분이없다면?. 시스템오류가발생할때까지무한정호출하게된다.
피보나치수열의계산 #1 순환호출을사용하면비효율적인예 피보나치수열 0,1,1,2,3,5,8,13,21, fib ( n ) fib ( n 0 1 2) fib ( n 1) n 0 n 1 otherwise 순환적인구현 int fib(int n) if( n==0 ) return 0; if( n==1 ) return 1; return (fib(n-1) + fib(n-2));
피보나치수열의계산 순환호출을사용했을경우의비효율성 같은항이중복해서계산됨 예를들어 fib(6) 을호출하게되면 fib(3) 이 4번이나중복되어서계산됨 이러한현상은 n이커지면더심해짐 fib(6) fib(4) fib(5) fib(2) fib(3) fib(3) fib(4) fib(2) fib(3) fib(1) fib(2) fib(1) fib(2) fib(2) fib(3)
하노이탑문제 #1 문제는막대 A에쌓여있는원판 3개를막대 C로옮기는것이다. 단다음의조건을지켜야한다. 한번에하나의원판만이동할수있다 맨위에있는원판만이동할수있다 크기가작은원판위에큰원판이쌓일수없다. 중간의막대를임시적으로이용할수있으나앞의조건들을지켜야한다. A B C
3 개의원판인경우의해답 A B C A B C A B C A B C A B C A B C A B C A B C
n 개의원판인경우 n-1 개의원판을 A 에서 B 로옮기고 n 번째원판을 A 에서 C 로옮긴다음, n-1 개의원판을 B 에서 C 로옮기면된다. n-1 개의원판 1 개의원판 A B C A B C A B C A B C
하노이탑알고리즘 // 막대 from에쌓여있는 n개의원판을막대 tmp를사용하여막대 to로옮긴다. void hanoi_tower(int n, char from, char tmp, char to) if (n == 1) from에서 to로원판을옮긴다. else hanoi_tower(n-1, from, to, tmp); from에있는한개의원판을to로옮긴다. hanoi_tower(n-1, tmp, from, to);
하노이탑실행결과 #include <stdio.h> void hanoi_tower(int n, char from, char tmp, char to) if( n==1 ) printf(" 원판 1 을 %c 에서 %c 으로옮긴다.\n",from,to); else hanoi_tower(n-1, from, to, tmp); printf(" 원판 %d 을 %c 에서 %c 으로옮긴다.\n",n, from, to); hanoi_tower(n-1, tmp, from, to); int main(void) hanoi_tower(4, 'A', 'B', 'C'); return 0; 원판 1 을 A 에서 B 으로옮긴다. 원판 2 을 A 에서 C 으로옮긴다. 원판 1 을 B 에서 C 으로옮긴다. 원판 3 을 A 에서 B 으로옮긴다. 원판 1 을 C 에서 A 으로옮긴다. 원판 2 을 C 에서 B 으로옮긴다. 원판 1 을 A 에서 B 으로옮긴다. 원판 4 을 A 에서 C 으로옮긴다. 원판 1 을 B 에서 C 으로옮긴다. 원판 2 을 B 에서 A 으로옮긴다. 원판 1 을 C 에서 A 으로옮긴다. 원판 3 을 B 에서 C 으로옮긴다. 원판 1 을 A 에서 B 으로옮긴다. 원판 2 을 A 에서 C 으로옮긴다. 원판 1 을 B 에서 C 으로옮긴다.
이진수출력하기 정수를이진수로출력하는프로그램작성 순환알고리즘으로가능
가변매개변수 // 2진수형식으로출력 #include <stdio.h> void print_binary(int x); int main(void) print_binary(9); return 0; void print_binary(int x) if( x>0) print_binary(x / 2); printf("%d", x % 2); // 순환호출 1001 // 나머지를출력
Q & A