제군들!! 우린 거의 다 왔다!!
이 고비만 넘기면 포인터란놈을 맞이하러 간다!!
모자란 머리로 여기까지 온것 칭찬한다!!
함수놈의 머리를 부수고 우린 포인터를 잡으러 간다!!
베헤에에엥라라아아아앙!
아 그런데 이쯤 되니, 내가 우리 바바리안들을 모두 끌고 가기 힘들어 져서 새로운 선생을 모셔왔다!!인사해라!!
우리보다 더 바보인 해골바가지들을 교육하는 암흑 마녀다!!
암흑마녀는 주석에 설명을 표시한다고 한다!! 주석을 잘봐야 한다!!
그럼 나도 수업 들으러 간다!!
베헤헤헤에에에엘라!!!!
함수의 정의
C 언어로 프로그래밍을 하다 보면 들어가는 값만 바뀔 뿐 같은 코드가 계속 반복되는 경우가 많습니다. 특히 같은 코드를 반복해서 작성하면 코드도 길어지고 중간에 실수할 가능성이 높아집니다.
C 언어에서는 함수(function)라는 기능을 제공하는데 특정 용도의 코드들을 한 곳에 모아놓은 것을 뜻합니다. 그래서 처음 한 번만 작성해놓으면 나중에 필요할 때 계속 불러 쓸 수 있습니다. 예를 들어 지금까지 사용했던 printf, scanf 등도 모두 C 언어에서 미리 만들어둔 함수입니다.
함수는 다양한 사용 방법이 있으므로 이제부터 각 유닛에서 함수의 사용법을 자세히 알아보겠습니다.
함수를 정의하는 방법은 다음과 같이 반환값 자료형, 함수 이름, ( ) (괄호)순으로 적어준 뒤 { } (중괄호) 안에 원하는 코드를 작성하면 됩니다(함수 이름을 짓는 방법은 변수 이름을 짓는 방법과 같습니다).
반환값자료형 함수이름()
{
코드;
}
다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
function.c
#include <stdio.h>
void hello() // 반환값이 없는 hello 함수 정의
{
printf("Hello, world!\n"); // Hello, world! 출력
}
int main()
{
hello(); // hello 함수 호출
return 0;
}
실행 결과
Hello, world!
이제 함수를 만들려면 main 함수 바깥에서 작성해야 합니다. 다음과 같이 main 함수 위에서 반환값의 자료형은 void, 이름은 hello인 함수를 정의했습니다. 그리고 중괄호 안에서 printf로 "Hello, world!" 문자열을 출력했습니다.
void hello() // 반환값이 없는 hello 함수 정의
{
printf("Hello, world!\n"); // Hello, world! 출력
}
여기서 함수의 반환값 부분에 void를 적어주면 함수의 반환값이 없다는 뜻이 됩니다. 즉, void는 함수의 반환값이 없음을 나타낼 때와 포인터로 사용할 때의 의미가 다릅니다. 이 부분을 잘 기억해두세요.
함수를 만들었으니 사용을 해봐야겠죠? main 함수 안에서 hello();와 같이 함수 이름과 ()를 적어준 뒤 ; (세미콜론)을 붙이면 함수를 사용할 수 있습니다.
int main()
{
hello(); // hello 함수 호출
return 0;
}
이렇게 함수를 사용하는 방법을 "함수를 호출(call)한다"라고 부르기도 합니다.
다음은 main 함수와 hello 함수의 실행 순서입니다.
- main 함수 실행
- hello 함수 호출
- hello 함수 실행
- printf 함수 실행 및 "Hello world!" 출력
- hello 함수 종료
- 0 반환 및 main 함수 종료
▼ 그림 45‑1 main 함수와 hello 함수의 실행 순서
지역변수
함수 안에 선언된 변수를 지역 변수라고 부르는데 이 지역 변수의 특징은 함수가 끝나면 사라진다는 점입니다.
void hello()
{
int num1 = 10; // 지역 변수. hello 함수가 끝나면 사라짐
printf("Hello, %d", num1);
}
int main()
{
hello();
printf("%d", num1); // 컴파일 에러: hello 함수의 지역 변수 num1은
// 함수 hello 안에서만 사용할 수 있음
return 0;
}
hello 함수 안에 선언된 지역 변수 num1은 hello 함수 안에서만 사용할 수 있고, 함수 바깥에서는 사용할 수 없습니다.
//함수의 전달인자
/* lethead2.c */
#include <stdio.h>
#include <string.h> /* streln()을 사용하기 위해 */
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
#define SPACE ' '
void show_n_char(char ch, int num);
int main(void)
{
int spaces;
show_n_char('*', WIDTH); /* 기호 상수를 전달인자로 사용한다. */
putchar('\n');
show_n_char(SPACE, 12); /* 기호 상수를 전달인자로 사용한다. */
printf("%s\n", NAME);
spaces = (WIDTH - strlen(ADDRESS)) / 2;
/* 몇 개의 스페이가 필요한지 */
/* 프로그램이 계산하게 한다. */
show_n_char(SPACE, spaces); /* 변수를 전달인자로 사용한다. */
printf("%s\n", ADDRESS);
show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
/* 표현식을 전달인자로 사용한다. */
printf("%s\n", PLACE);
show_n_char('*', WIDTH);
putchar('\n');
return 0;
}
/* show_n_char() 함수의 */
void show_n_char(char ch, int num)
{
int count;
for (count = 1; count <= num; count++)
putchar(ch);
}
return값을 가진 함수.
//return을 이용하여 함수로부터 값 리턴하기
/* lesser.c -- 둘 중에서 죽은 것을 구한다. */
#include <stdio.h>
int imin(int, int);
int main(void)
{
int evil1, evil2; //int형 변수 선언
printf("두 정수를 입력하시오(끝내려면 q):\n");
while (scanf("%d %d", &evil1, &evil2) == 2) //입력 값을 변수에 저장
{
printf("(%d,%d)에서 작은 것은 %d.\n", //변수를 출력하고,
evil1, evil2, imin(evil1,evil2)); //함수를 출력
printf("두 정수를 입력하시오(끝내려면 q):\n");
}
printf("종료!.\n");
return 0;
}
int imin(int n,int m)
{
int min; //int 형 변수 선언
if (n < m) //조건 : n보다 m이 적다면,
min = n; //참일때 : n을 min에 대입한다.
else
min = m; //거짓일 때 : n을 min에 대입한다.
return min; //min의 값을 int imin으로 리턴한다.
}
- return값을 쓰는 함수는 결과 값을 return해주면 된다.
- 그럼 main함수에서 그 값을 출력하면 된다.
부정확한 함수의 예
//ANSI C 함수 프로토타입
// misuse.c -- 부정확하게 함수를 사용한다.
#include <stdio.h>
int imax(); /* ANSI C 이전 형식의 함수 선언 */
int main(void)
{
printf("(%d,%d)에서 큰 것은 %d.\n", //함수의 매개변수를 두개로 정했으므로, 전달인자를 두개를 줘야 한다.
3, 5, imax(3)); //그런데 여기선 전달인자를 하나만 입려했기 때문에 에러가 발생한다. 이것을 데이터형 불일치 에러라고 한다.
printf("(%d,%d)에서 큰 것은 %d.\n", //여기선 함수의 매개와 전달인자를 입력했지만.
3, 5, imax(3.0, 5.0)); //그런데 여기선 함수는 전달형인데, 전달인자가 실수형이라 오류가 발생한다.
return 0;
}
//함수의 정의
int imax(n, m)
int n, m;
{
return (n > m ? n : m); // 리턴에다 표현식을 써도 된다!
// '?'=3항 연산자라고 함. '?'왼쪽에 조건을 걸고,
// 오른쪽에 참일 땐 n값을 출력하게, 거짓일 땐 m값을 출력한다.
}
- 이렇게 잘못된 부정확 함수 사용하게 되면 값깨진 채로 출력되버린다.
//ANSI C해결책
//proto.c --함수 프로토타입을 사용한다.
#include <stdio.h>
int imax(int, int); //매개변수에 전달인자 int형을 넣어준다.
int main(void)
{
printf("(%d,%d)에서 큰 것은%d\n",
3, 5, imax(3,5)); //데이터형 불일치 해소
printf("(%d,%d)에서 큰 것은%d\n",
3, 5, imax(3.0, 5.0)); //에러메지는 뜨지 않았지만, double형에서 int형으로 바꼈기 때문에
return 0; //소수점 이하의 수는 모두 잘린다.
}
int imax(int n, int m)
{
return (n > m ? n : m);
}
- 위와 같이 바꿔주면 해결된다.
&연산자
//주소알아내기: &연산자
/* loccheck.c -- 변수들이 어디에 저장되는지 확인한다 */
#include <stdio.h>
void mikado(int); /* 함수선언 */
int main(void)
{
int pooh = 2, bah = 5; /* main의 지역변수 */
printf("main()에 있는 pooh = %d, &pooh = %p\n", // &는 어떤 변수가 저장되어 있는 주소를 알아낸다.
pooh, &pooh); // 포인터의 포맷지정자는 %p이다.
printf("mian()에 있는 bah = %d, &bah = %p\n", //
bah, &bah);
mikado(pooh);
return 0;
}
void mikado(int bah) /* 함수정의 */ //함수의 배개변수에 intbath를 선언했다.
{
int pooh = 10; /* mikado()의 지역변수 */
printf("mikado()에 있는, pooh = %d, &pooh = %p\n", //
pooh, &pooh);
printf("mikado()에 있는, bah = %d, &bah = %p\n", //
bah, &bah);
}
- 이 예제를 통해 우린 %p와 &변수 를사용하면 해당 변수의 값이 저장되어 있는 위치를 알 수 있다.
- 방금 너희들은 포인터를 사용한거다. 변수의 값이 저장되어 있는 위치를 불러 오는 것이 포인터다.
- 이걸 어디 써먹는지 궁금한 바바리안 들이 많을 것이다. 자, 다음 예제를 통해 알려주겠다.
호출 함수에 있는 변수 바꾸기(포인터 사용)
//호출 함수에 있는 변수 바꾸기
/* swap1.c -- 맞교환 함수 제1 버전 */
#include <stdio.h>
void interchange(int u, int v); /* 함수 선언 */
int main(void)
{
int x = 5, y = 10; //전달인자 변수 데이터현 선언
printf("교환전 x = %d, y = %d.\n", x , y); //포맷 지정자와 전달인자 변수 일치.
interchange(x, y); //함수 사용(x,y 바꾸기) 시도.
printf("교환후 x = %d, y = %d.\n", x, y); //포맷 지정자와 전달인자 변수 일치.
return 0;
}
void interchange(int u, int v) /* 함수정의 */
{
int temp;
temp = u;
u = v;
v = temp;
}
- 자, 겉보기엔 뭐가 문제인지 모를거다.
- 바바리안들이 딱 보고 이해하기엔 조금 어려울수 있다. 당황하지 마라.
- 그래서 친히 다음 예제를 통해 디버깅을 시켜주겠다.
//swap2.c -- swap1.c 에서 무엇이 잘못되었는지 조사한다.
#include <stdio.h>
void interchange(int u, int v);
int main(void)
{
int x = 5, y = 10; //전달인자 변수 데이터현 선언
printf("교환전 x = %d, y = %d.\n", x , y);//포맷 지정자와 전달인자 변수 일치.
interchange(x, y);//함수 사용(x,y 바꾸기)
printf("교환후 x = %d, y = %d.\n", x, y);//포맷 지정자와 전달인자 변수 일치.
return 0;
}
//함수의 정의
void interchange(int u, int v)
{
int temp; //temp 정수형 변수 선언
printf("교환전 u = %d, v = %d.\n", u , v);
temp = u;
u = v;
v = temp;
printf("교환후 u = %d, v = %d.\n", u, v);
}
- interchange()함수안에서는 u와 v값이 맞교환 되었다. 하지만, main에서는 다른 변수를 사용한다.
- 그래서 u와 v의 값을 맞교환하는 것은 x와 y에 영향을 주지 못한다.
- 리턴 값을 주는 함수가 아니기에, 지역변수에 불과한 u와 v가 전달인자인 x와 y값에 영향력을 끼치지 못하기 때문이다.
- 그럼 return값을 준다면 어떻게 될까?
- return값을 준다해도 크게 달라질건 없다.
- return은 값을 한개밖에 돌려주지 못하기 때문에, u나 v중 하나만 변경이 가능하다.
- 따라서 우리는 포인터를 쓸수 밖에 없다는 것이다.
//함수 간의 커뮤니케이션에 포인터 사용하기
//swqp.c--포인터를 사용하여 맞교환을 바르게 수행한다.
#include <stdio.h>
void interchange(int * u, int * v); //' * '는 이 변소가 포인터라는 것을 나타낸다.
int main(void)
{
int x=5, y =10;
printf("교환전 x =%d, y= %d\n", x, y);
interchange(&x,&y); //함수에 주소(가르키는 값)를 전달한다.
printf("교환후 x=%d, y=%d\n", x, y);
return 0;
}
void interchange(int * u, int * v)
{
int temp;
temp = *u; // temp는 u가 가르키는 값을 얻는다.
*u=*v; //y는 v가 가르키는 값을 얻고,
*v=temp; //v는 다시 temp가 가르키는 값을 얻게된다.
}
'C' 카테고리의 다른 글
C기초 플러스 chap.11 문자열과 문자열 함수 (0) | 2024.08.12 |
---|---|
C기초 플러스 chap.10 배열과 포인터 (0) | 2024.08.12 |
C기초 플러스 chap.7 C의 제어문: 분기와 점프 (0) | 2024.08.11 |
C기초 플러스 chap.6 C의 제어문: 루프 (0) | 2024.08.09 |
C기초 플러스 chap.5 연산자, 표현식, 문자 (1) | 2024.08.08 |
댓글