2019-1 st 프로그래밍입문 (1) 6 장. 함수 박종혁교수 서울과학기술대학교컴퓨터공학과 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr
목차 함수의개념 함수의필요성 함수의종류 함수의기본 함수의정의 함수의호출 함수의선언 지역변수와전역변수 지역변수 전역변수 변수의영역규칙 표준 C 라이브러리함수 2
함수란 특정기능을제공하는일련의코드를묶어서이름을붙인것 일종의블랙박스 전달하는인자의의미와호출결과만알면된다 3
함수란 하향식프로그래밍기법 프로그램은하나이상의함수로구성됨 함수정의 함수가수행할일을기술한 C 코드 함수정의의일반적인형식 type function_name( parameter list ) declarations statements 4
함수의필요성 5
함수를사용할때의장점 코드가중복되지않으므로간결하고알아보기쉽다 한번작성해둔코드를여러번사용하므로코드의재사용성이높다 기능위주로함수를작성해서사용하므로프로그램의모듈화가증대된다 함수코드를수정하더라도함수를호출하는부분은수정할필요가없으므로프로그램을유지보수하기쉽다 6
함수의종류 7
함수의요건 함수의정의 (definition) : 함수가수행할내용을기술 함수의호출 (call) : 이미정의된함수를사용 함수의선언 (declaration) : 사용될함수에대한정보를미리제공 8
함수의정의 9
함수의실행과정 함수는호출하기전에정의되어있어야한다 다른함수가먼저있어도프로그램은항상메인함수부터시작된다 10
함수의실행과정 메인함수의실행중에다른함수를호출하면그때함수가실행된다 함수가호출될때전달인자는매개변수에복사된다 11
함수의실행과정 함수가실행을마치고리턴할때는제어와함께리턴값도돌려준다 함수가리턴하는값은복사되어임시기억공간에저장되며, 이값은따로저장하지않으면버려지므로다른변수에저장해서사용한다 int res; //sum 함수가리턴하는값이 int 형이므로 int 형변수를선언한다. res = sum(10,20); //sum 함수가리턴하는값을저장한다. 12
제어의흐름예제 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 13
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 14
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 15
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 16
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 17
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 18
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 19
제어의흐름 #include <stdio.h> int test(int); int main(void) int b, a; int test(int c) c = c + 10; return 1; a = 5; b = test(a); printf("test is %d", b); return 0; int printf(... )..... 20
함수의리턴형 함수가처리결과로리턴하는값의데이터형 함수가처리결과로리턴하는값이없을때는 void 라고적어준다 함수는반드시하나의값만리턴할수있다 21
함수의이름 (1/2) 어떤일을하는함수인지명확하게알수있는이름을사용한다 식별자를만드는규칙에따라서정해야한다 일관성있는이름을사용하는것이좋다 22
함수의이름 (2/2) 서로다른함수가같은이름을사용할수없다 명명규칙 (Naming Convention) 식별자로사용되는이름을정할때적용되는규칙 프로그래머가직접자신만의명명규칙을정하고따르면코드의가독성을높일수있다 23
매개변수목록 (1/2) 매개변수 (parameter) 함수를호출한곳에서함수안으로전달되는값을보관하기위한변수 인자, 인수 (argument) 함수를호출할때, 실제로전달되는값 24
매개변수목록 (2/2) 매개변수의개수에는제한이없다 매개변수가없을때는 void 를써주는데, 이때의 void 는생략할수있다 함수를정의할때, 매개변수의데이터형을생략하거나매개변수의이름을생략하면컴파일에러가발생한다 25
함수의내용 함수의정의는헤더 (header) 와바디 (body) 로구성된다 26
함수헤더 헤더 함수정의에서첫번째여는중괄호의앞부분 type function_name(parameter list) type - 함수가리턴하는값의형 - 컴파일러는필요하다면, 함수의리턴값을이 type 으로변환함 - 이것이 void 이면리턴하는값이없다는것을나타냄 parameter list - 이함수가가지는인자의목록 - 이함수를호출할때에는이 list 에맞게호출해야함 - 이것이 void 이면인자를갖지않음을나타냄 27
함수몸체 몸체 함수정의에서중괄호사이에있는문장들 예제 int factorial(int n) /* header */ /* body starts here */ int i, product = 1; for (i = 2; i <= n; ++i) product *= i; return product; 28
리턴값과매개변수가없는함수 리턴형과매개변수목록을 void 로지정한다 void 형의함수를호출하면정해진코드를수행하고, 함수의끝을만나면리턴한다 29
리턴값은없고매개변수만있는함수 (1/2) 선그리는기능 30
리턴값은없고매개변수만있는함수 (2/2) count 개의정수를입력받아합계를구해서출력하는함수 31
리턴값과매개변수가있는함수 리턴형이있는함수에서 return 문을생략하면안된다 num 팩토리얼을구하는함수 원의면적을구하는함수 32
기존의코드로부터함수를정의하는과정 어떤내용을어떻게만들것인가? 33
기존의코드로부터함수를정의하는과정 어떤내용을어떻게만들것인가? 34
기존의코드로부터함수를정의하는과정 35
처음부터함수를정의하는과정 36
함수의호출 이미만들어진함수를불러쓰는것 함수이름다음에 ( ) 를쓰고, ( ) 안에함수의인자를써준다 함수를호출할때넘겨준인자가매개변수로전달된다 37
리턴값과매개변수가없는함수의호출 hi(); 처럼함수이름다음에빈괄호를적어준다 ( ) 가없으면함수호출이아니다 38
예제 : 리턴값과매개변수가없는함수의사용예 39
리턴값은없고매개변수만있는함수의호출 40
예제 : 매개변수가있는 draw_line 함수의사용예 41
인자전달시주의사항 ( ) 안에함수의인자를콤마 (,) 로나열한다 함수호출시넘겨준인자는함수의매개변수로순서대로전달된다 매개변수의데이터형과같은형의값을인자로전달해야한다 인자와매개변수의데이터형과일치하지않으면형변환해서전달한다 인자와매개변수의순서와개수가일치해야한다 매개변수의의미에맞게순서대로인자를전달해야한다 char 형으로형변환 인자와매개변수의개수가다르므로컴파일에러 인자의순서가잘못된경우 42
예제 : 매개변수가있는 print_sum 함수의사용예 43
리턴값이없는함수호출시주의사항 리턴형이 void 형인함수는리턴값이없으므로수식이아니다 수식이사용되어야하는곳에리턴값이없는함수호출은사용할수없다 44
리턴값과매개변수가있는함수의호출 (1/2) 함수의리턴값은임시값이므로사용하지않으면사라진다 함수의리턴값은변수에저장할수도있고, 다른수식의일부분이나다른함수호출의인자로사용할수있다 45
리턴값과매개변수가있는함수의호출 (2/2) 46
예제 : get_factorial 함수의사용예 47
예제 : 원의면적을구하는 get_area 함수사용예 48
인자전달과정의의미 49
예제 : 두정수의최대공약수를구하는 get_gcd 함수의사용예 50
함수호출시주의사항 (1/3) 함수의인자도수식이며, 항상인자의값을먼저평가한다 51
예제 : get_max 함수의사용예 52
함수호출시주의사항 (2/3) 인자의개수와데이터형은매개변수와일치해야한다 53
함수호출시주의사항 (3/3) 함수는이름으로구분한다 같은이름의함수를여러번정의할수없다 54
함수의선언 함수가정의된위치에상관없이호출하려면함수의선언이필요하다 함수의리턴형, 이름, 매개변수에대한정보를미리알려준다 함수의원형 (prototype) 함수의시그니쳐 (signature) 55
함수선언문 56
함수선언문의위치 57
예제 : draw_line, get_area 함수의선언 58
선언 / 정의되지않은함수의호출 59
main 함수앞에함수정의를할경우선언필요없음 - 단, 많은함수정의를할경우프로그램가독성저해우려 60
지역변수와전역변수 지역변수 (local variable) : 함수나블록안에선언되는변수 전역변수 (global variable) : 함수밖에선언되는변수 61
지역변수 ANSI C 에서는지역변수는블록의시작부분에선언해야한다 C99 에서는원하는위치에지역변수를선언할수있다 지역변수가선언된위치에따라지역변수의사용범위가결정된다 62
지역변수의생성과소멸 함수안에선언된지역변수 함수가호출되는횟수만큼생성되고소멸된다 void dummy() 함수호출시 매번다시생성 int y = 456; printf("y = %d\n", y); y--; 함수리턴시소멸 int main(void) int i; for (i = 0; i < 3; i++) dummy(); y = 456을 3번출력 반복문의블록안에선언된지역변수 반복문이수행되는횟수만큼생성되고소멸된다 for (i = 0; i < 3; i++) 각반복회차마다 int x = 123; 매번다시생성 printf("x = %d\n", x); x++; 블록의끝을만나면소멸 x = 123 을 3 번출력 63
예제 : 지역변수의생성과소멸과정 void dummy() int y = 456; printf("y = %d\n", y); y--; 감소된 y 는함수리턴시소멸 함수호출시매번다시생성 for 의각반복회차가시작될때마다생성 int main(void) int i; for (i = 0; i < 3; i++) dummy(); for (i = 0; i < 3; i++) int x = 123; printf( x = %d\n, x); x++; 증가된 x 는블록의끝을만나면소멸 64
함수의매개변수 함수의매개변수도지역변수다 함수가호출될때생성되고함수가리턴할때소멸된다 함수안에서함수의매개변수를변경해도함수를호출하는쪽에아무영향도주지않는다 매개변수를변경하는것은의미없다 void double_it(int num) num *= 2; int main(void) int x = 5; 함수호출후 x는 double_it(x); 변경되지않는다. printf("x = %d\n", x); x = 5 출력 int double_this(int num) return num * 2; 함수를호출하는 쪽으로전달 int main(void) 함수의리턴값을 x에받아온다. int x = 5; x = double_this(x); printf( x = %d\n, x); x = 10 출력 65
예제 : 지역변수로서의매개변수 66
지역변수의사용범위 서로다른함수에서같은이름의변수를선언하면, 이름은같지만서로다른변수가된다 각각의함수는자신안에선언된변수만사용한다 67
예제 : 지역변수의사용범위 68
전역변수의선언 전역변수는보통소스파일의시작부분에선언한다 전역변수는따로초기화하지않으면자동으로 0 으로초기화된다 69
전역변수 전역변수는프로그램이시작될때한번만생성되고, 프로그램이종료될때소멸된다 소스파일의시작부분에전역변수를선언하면, 같은소스파일의모든함수에서전역변수를바로사용할수있다 70
예제 : 전역변수의선언및사용 void print_count(void); void increment(void); void decrement(void); int count; // 전역변수선언 int main(void) int i; count = 0; print_count(); for (i = 0; i < 3; i++) increment(); print_count(); for (i = 0; i < 3; i++) decrement(); print_count(); void print_count(void) printf("count = %d\n", count); void increment(void) count++; void decrement(void) count--; 71
변수의영역규칙 블록범위가다를때는같은이름의변수를여러번선언할수있다 가까운블록안에선언된변수가우선적으로사용된다 같은이름의지역변수가있을때는전역변수를사용할수없다 전역변수이름앞에 g_ 와같은접두사를사용해서지역변수명과구분한다 72
예제 : 변수의영역규칙 void test(void); double x = 0.01; // 전역변수 x int main(void) double x = 0.5; // main의지역변수 x while (1) double x = 1.23; // while 블록의지역변수 x printf("in while block: x = %f\n", x); // x는 1.23 if (x > 1) // x는 1.23 break; printf("in main block: x = %f\n", x); // x 는 0.5 test(); void test(void) x *= 10; // 전역변수 x printf("in test block: x = %f\n", x); 73
병렬블록과중첩블록 int a, b;... /* inner block 1 */ float b;... /* int a is known, but not int b */... /* inner block 2 */ float a;... /* int b is known, but not int a */ /* nothing in inner block 1 in known */... 74
디버깅을위한블록사용 블록은디버깅을위한목적으로많이사용 코드부분에임시로블록을삽입하면, 프로그램의다른부분에영향을주지않는지역변수를사용할수있음 v 가이상한값을갖는다고가정 /* debugging starts here */ static int cnt = 0; printf("*** debug : cnt = %d v = %d\n", ++cnt, v); 75
메모리영역 76
메모리영역 push 동작 pop 동작 77
재귀함수 함수내부에서자기자신의함수를다시호출 ( 재귀호출 ) 하는순환호출함수 복잡한알고리즘을간략하게구현가능 재귀는각호출을위한인자와변수를스택에쌓아두어관리하기때문에많은시간과공간을요구함 즉, 재귀를사용할때에는비효율성을고려해야함 그러나일반적으로재귀적코드는작성하기쉽고, 이해하기쉬우며, 유지보수하기가쉬움 78
재귀함수 단순한재귀적루틴은일반적인패턴을따름 재귀의일반적인패턴에서는기본적인경우와일반적인재귀경우를처리하는코드가있음 보통두경우는한변수에의해결정됨 일반적인재귀함수의제어흐름 1. 변수를검사하여기본적인경우인지일반적인경우인지를결정 2. 기본적인경우일때에는더이상재귀호출을하지않고필요한값을리턴 3. 일반적인경우일때에는그변수의값이결국에기본적인경우의값이될수있게하여재귀호출 79
재귀함수예제 int sum(int n) if (n <= 1) // 1 번 return n; else return (n + sum(n - 1)); 위예제코드에서는 n 을사용하여두경우를판단 // 2 번 1. n 이 1 보다작거나같으면기본적인경우임 n 을리턴 2. 아니면일반적인경우임 n 에서 1 을빼어재귀호출 ==> n 에서 1 을뺐기때문에언젠가 n 은 1 보다작거나같아질것임 80
하노이탑문제 하노이탑문제는막대 A 에쌓여있는원판 3 개를막대 C 로동일한모양으로옮기는것 * 단다음의조건을지켜야함 한번에하나의원판만이동할수있다 맨위에있는원판만이동할수있다 크기가작은원판위에큰원판이쌓일수없다 중간의막대를임시적으로이용할수있으나앞의조건들을지켜야한다 81
하노이탑알고리즘 순서대로 1 부터 n 까지원판이있고 A, B, C 3 개의막대기가있는경우하노이탑문제를해결하는방법 1. A 에서 2 번부터 n 번째까지 n-1 개의원판을 B 로이동 2. A 에서 1 번원판을 C 로이동 3. B 에서 2 번부터 n 번째까지 n-1 개의원판을 C 로이동 82
하노이탑알고리즘 // 막대 from에쌓여있는 n개의원판을막대 tmp를사용하여막대 to로옮긴다. hanoi_tower(n, A, B, C ); // 하노이함수호출 void hanoi_tower(int n, char from, char tmp, char to) if (n == 1) else printf("board %d move %c->%c\n", n, from, to); hanoi_tower(n-1, from, to, tmp); printf("1board %d move %c->%c\n",n, from, to); hanoi_tower(n-1, tmp, from, to); 83
표준 C 라이브러리함수 C 컴파일러가필수로제공해야하는플랫폼독립적인함수집합 라이브러리함수를호출하려면 라이브러리헤더파일을포함해야한다 호출할함수의원형을알아야한다 라이브러리레퍼런스페이지 함수에대한대략적인설명 함수원형 매개변수및리턴값에대한설명 필요한헤더파일및요구사항 간단한사용예 84
<ctype.h> 85
<math.h> 86
<stdlib.h> 87
<string.h> 88
<time.h> 89
90
Making coffee 프로그램 int main() int coffee; printf(" 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) "); scanf_s("%d", &coffee); printf("\n# 1. 뜨거운물을준비한다 \n"); printf("# 2. 종이컵을준비한다 \n"); switch (coffee) case 1 : printf("# 3. 보통커피를탄다 \n"); break; case 2 : printf("# 3. 설탕커피를탄다 \n"); break; case 3 : printf("# 3. 블랙커피를탄다 \n"); break; default : printf("# 3. 아무거나탄다 \n"); break; printf("# 4. 물을붓는다 \n"); printf("# 5. 스푼으로저어서녹인다 \n\n"); printf(" 손님 ~ 커피여기있습니다.\n\n"); 커피종류입력 안내문출력 커피종류선택변수 91
Making coffee 프로그램 ( 모듈화 ) #include <stdio.h> int coffee_machine(int button) printf("\n# 1.( 자동으로 ) 뜨거운물을준비한다 \n"); printf("# 2. ( 자동으로 ) 종이컵을준비한다 \n"); switch (button) case 1 : printf("# 3. ( 자동으로 ) 보통커피를탄다 \n"); break; case 2 : printf("# 3. ( 자동으로 ) 설탕커피를탄다 \n"); break; case 3 : printf("# 3. ( 자동으로 ) 블랙커피를탄다 \n"); break; default : printf("# 3. ( 자동으로 ) 아무거나탄다 \n"); break; printf("# 4. ( 자동으로 ) 물을붓는다 \n"); printf("# 5. ( 자동으로 ) 스푼으로저어서녹인다 \n\n"); return 0; int main() int coffee; int ret; printf(" 어떤커피를드릴까요?(1: 보통, 2: 설탕, 3: 블랙 ) "); scanf("%d", &coffee); ret = coffee_machine(coffee); printf(" 손님 ~ 커피여기있습니다.\n\n"); 92
Making coffee 실행결과 실행결과 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) 2 # 1. 뜨거운물을준비한다 # 2. 종이컵을준비한다 # 3. 설탕커피를탄다 # 4. 물을붓는다 # 5. 스푼으로저어서녹인다손님 ~ 커피여기있습니다. 93
Making coffee 프로그램 확장 #include <stdio.h> int coffee_machine(int button) printf("\n# 1.( 자동으로 ) 뜨거운물을준비한다 \n"); printf("# 2. ( 자동으로 ) 종이컵을준비한다 \n"); switch (button) case 1 : printf("# 3. ( 자동으로 ) 보통커피를탄다 \n"); break; case 2 : printf("# 3. ( 자동으로 ) 설탕커피를탄다 \n"); break; case 3 : printf("# 3. ( 자동으로 ) 블랙커피를탄다 \n"); break; default : printf("# 3. ( 자동으로 ) 아무거나탄다 \n"); break; printf("# 4. ( 자동으로 ) 물을붓는다 \n"); printf("# 5. ( 자동으로 ) 스푼으로저어서녹인다 \n\n"); return 0; int main() int coffee; int ret; printf("a님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) "); scanf_s("%d", &coffee); ret = coffee_machine(coffee); printf("a님커피여기있습니다.\n\n"); printf("b님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) "); scanf ("%d", &coffee); ret = coffee_machine(coffee); printf("b님커피여기있습니다.\n\n"); printf("c님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) "); scanf_s("%d", &coffee); ret = coffee_machine(coffee); printf("c님커피여기있습니다.\n\n"); 94
Making coffee ( 확장 ) 실행결과 A 님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) 1 # 1. ( 자동으로 ) 뜨거운물을준비한다 # 2. ( 자동으로 ) 종이컵을준비한다 # 3. ( 자동으로 ) 보통커피를탄다 # 4. ( 자동으로 ) 물을붓는다 # 5. ( 자동으로 ) 스푼으로저어서녹인다 A 님커피여기있습니다. B 님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) 2 # 1. ( 자동으로 ) 뜨거운물을준비한다 # 2. ( 자동으로 ) 종이컵을준비한다 # 3. ( 자동으로 ) 설탕커피를탄다 # 4. ( 자동으로 ) 물을붓는다 # 5. ( 자동으로 ) 스푼으로저어서녹인다 B 님커피여기있습니다. C 님, 어떤커피드릴까요? (1: 보통, 2: 설탕, 3: 블랙 ) 95
더하기함수 #include <stdio.h> int plus(int v1, int v2) int result; result = v1 + v2; 실행결과 return result; 100과 200의 plus() 함수결과는 : 300 int main() int hap; hap = plus(100, 200); printf("100과 200의 plus() 함수결과는 : %d\n", hap); 96
계산기함수 #include <stdio.h> int calc(int v1, int v2, int op) int result; switch (op ) case 1 : result = v1 + v2; break; case 2 : result = v1 - v2; break; case 3 : result = v1 * v2; break; case 4 : result = v1 / v2; break; return result; int main() int res; int oper, a, b; printf(" 계산입력 (1:+, 2:-, 3:*, 4:/) : "); scanf("%d", &oper); printf(" 계산할두숫자를입력 : "); scanf("%d %d", &a, &b); res = calc(a, b, oper); printf(" 계산결과는 : %d\n", res); 97
계산기함수실행결과 실행결과 계산입력 (1:+, 2:-, 3:*, 4:/) : 3 계산할두숫자를입력 : 7 8 계산결과는 : 56 98
지역변수와전역변수 #include <stdio.h> int a = 100; void func1() int main() int a = 200; printf("func1() 에서 a 의값 ==> %d\n", a); func1(); printf("main() 에서 a 의값 ==> %d\n", a); 실행결과 func1() 에서 a 의값 ==> 200 main() 에서 a 의값 ==> 100 99
함수의반환값에따른차이 #include <stdio.h> void func1() printf("void 형함수는돌려줄게없음.\n"); int func2() return 100; int main() int a; func1(); a = func2(); printf("int 형함수에서돌려준값 ==> %d\n", a); 실행결과 void 형함수는돌려줄게없음. int 형함수에서돌려준값 ==> 100 100
매개변수전달방법 call by value #include <stdio.h> void func1(int b) void main() b = b + 1; printf(" 전달받은 a ==> %d\n", b); int a=10; func1(a); printf("func1() 실행후의 a ==> %d\n", a); 실행결과전달받은 a ==> 11 func1() 실행후의 a ==> 10 101
매개변수전달방법비교 #include <stdio.h> void func1 (char a, char b) int imsi; imsi = a; a = b; b = imsi; void func2 (char *a, char *b) int imsi; imsi = *a; *a = *b; *b = imsi; void main() char x = 'A', y = 'Z ; printf(" 원래값 : x=%c, y=%c\n", x, y); func1(x, y); printf(" 값을전달한후 : x=%c, y=%c\n", x, y); func2(&x, &y); printf(" 주소를전달한후 : x=%c, y=%c\n", x, y); 원래값값을전달한후주소를전달한후 실행결과 : x=a, y=z : x=a, y=z : x=z, y=a 102
103
참고문헌 천정아, Core C Programming, 연두에디션 (2019) C 가보이는그림책, ANK Co., Ltd., 성안당 (2018) Greg Perry, Dean Miller 어서와 C 언어는처음이지, 천인국옮김, 인피니티북스 (2015) KELLEY ( 역 : 김명호외 ), A Book on C, 홍릉과학출판사 (2003) 윤성우, 열혈 C 프로그래밍, 오렌지미디어 천인국, 쉽게풀어쓴 C 언어 Express, 생능출판사 서현우, 뇌를자극하는 C 프로그래밍, 한빛미디어 강성수, 쾌도난마 C 프로그래밍, 북스홀릭 고응남, C 프로그래밍기초와응용실습, 정익사 우재남, C 언어 for Beginner, 한빛미디어 104