프롤로그
ELITE HACKER Bootcamp 3rd 3주차 수업 공부 내용
aws 우분투 서버 하나 파서 연습
함수의 개념, 용도, 선언, 정의(프로토타입), 매개변수, 반환값, 가변인자, 스코프, 재귀 함수, 함수 포인터, 가변인자
함수
중고등학생 때에도 함수에 대해서 많이 배웠다.
y = f(x)
수학적용어로 함수라고 하는 것은, x의 값에 따라 y의 값이 하나로 나오는 관계를 함수라고 한다.
좀 더 전문적으로 말하면...
정의역의 원소마다 공역의 원소가 오직 하나씩 대응대는 관계..
그래서 만약에 f(x) 가 3x+1이라고 하면
y = 3x+1
이러한 함수에서, x에 값에 어떠한 값을 넣어도 y는 무조건 하나만 나오게 된다.
이 수학적 함수가 코드에서도 거의 비슷하다.
코드 세계에서 함수란, 어떠한 기능을 하는/특정 작업을 수행하는 코드 블록(묶음)이다. 왜 이 코드를 묶느냐? 같은 코드를 반복해야할 때, 재사용하기 위해서 사용한다.
그리고 a라는 기능을 하는 코드가 100줄정도 된다고 했을 때, func_a 라는 이름으로 재사용하기 때문에, 코드 가독성이 좋아진다.
그리고 함수 a가 고장났을 때, 딱 그것만 고치면 되니까 유지보수도 좋아지기에 함수 기능을 사용한다.
함수의 정의과 선언
파이썬 같은 경우는
def a():
print("a")
return
이런식으로 함수를 정의한다.사용하는데
아, 뭔 근본없는 언어 얘기를 하냐...C언어 함수 선언/정의하는 것을 봐보자.
// 함수 정의
반환형 함수명(자료형 매개변수1, 자료형 매개변수2, ...) {
return 반환값;
}
이렇게 보면 모를 수도 있겠다. 예시를 들어보자.
int add(int a, int b){
int c = a+b;
return c;
}
일단 add 부분은 함수 이름이라는 것은 알겠고, { } 중괄호 내부에서 함수가 실행되는 것도 알겠고,
그리고 코드가 다 실행되면, 맨 마지막에 return이 된다! 정도는 바로 이해할 수 있을 것이다.
함수의 구조는 조금 이따가 알아보도록 하고, 그래서 저거를 이제 실제로 실행을 시켜보도록 하자.
#include <stdio.h>
int add(int a, int b){
int c = a+b;
return c;
}
int main(){
int num1, num2;
printf("a의 값을 입력하세요 : ");
scanf("%d", &num1);
printf("b의 값을 입력하세요 : ");
scanf("%d", &num2);
int result = add(num1, num2);
printf("덧셈 결괴 : %d\n", result);
return 0;
}
함수는 기존 main함수 위쪽에다가 정의를 하고, 밑에서 함수이름 그대로 사용하여서 실행시켜주었다.
잘 실행되는 것이 보인다. 이렇게 함수이름 그대로 사용하여서 사용하면 되는데, 아 물론 저기 () 이거 다 밑에서 알아볼 거다. 일단 함수 정의한 거를 main함수 위에다가 적은 건 알 수있는데, 만약에 빼기, 곱하기, 나누기 기능들, 추가적으로 더 긴 함수들이 main함수 위에 쌓여있다면? main함수 찾기가 힘들고 코드 확인하기가 힘들어질 수 있다.
그래서 보통은 저 함수 정의한 거를 main함수 밑으로 내린다.
#include <stdio.h>
int main(){
int num1, num2;
printf("a의 값을 입력하세요 : ");
scanf("%d", &num1);
printf("b의 값을 입력하세요 : ");
scanf("%d", &num2);
int result = add(num1, num2);
printf("덧셈 결괴 : %d\n", result);
return 0;
}
int add(int a, int b){
int c = a+b;
return c;
}
그냥 딱 add 함수를 main함수 밑으로 내렸을 뿐이다. 근데
컴파일 자체가 안 된다. 어 왜지? 함수만 맨 아래로 내렸을 뿐인데?
C언어는 컴파일을 할 때 코드의 맨 위에서부터 차례대로 컴파일을 진행한다.
기존에 함수 정의한 거는 위에 있었으니까, 바로 함수를 인식하고 알고있다가, main함수에서 함수 사용되면 알고있었기 때문에 사용시킬 수 있는데, 지금은 main함수 밑으로 내려가버려서 함수를 인식하지 않고 있기 때문에 add라는 함수 사용하려고 코드 짜놓은 거를 오류가 발생했다고 하는 거다.
어쨌거나 함수가 길어지니까 밑으로 내려야하는 건 맞는데, 함수가 있다고 인식은 시켜야한다. 그거를 위하여 함수 선언을 맨 위에서 해줘야한다. 이를 프로토타입(함수 선언) 이라고 한다.
#include <stdio.h>
int add(int a, int b); // 함수 선언(프로토타입)
int main(){
int num1, num2;
printf("a의 값을 입력하세요 : ");
scanf("%d", &num1);
printf("b의 값을 입력하세요 : ");
scanf("%d", &num2);
int result = add(num1, num2);
printf("덧셈 결괴 : %d\n", result);
return 0;
}
int add(int a, int b){
int c = a+b;
return c;
}
맨 윗줄에 int add(int a, int b);
딱 이거 한 줄 추가해줬다. 내부 내용물 다 필요없고, 그냥 반환값, 함수명, 매개변수들만 적어둔 것이다. 이를 프로토타입이라고 하고, 이를 실행시키게 되면
그제서야 잘 실행이 된다.
정리해보면, 함수 정의는 맨 위에서 해버리게되면, 함수 선언이 필요가 없음. 다만, 함수가 길어져서 맨 아래에서 정의를 내릴 경우 프로토타입인 함수 선언을 맨 위에서 해주어야한다!!
return
다시 add 함수를 확인해보자.
int add(int a, int b){
int c = a+b;
return c;
}
그리고 main함수도 확인해보자.
int main(){
int num1, num2;
printf("a의 값을 입력하세요 : ");
scanf("%d", &num1);
printf("b의 값을 입력하세요 : ");
scanf("%d", &num2);
int result = add(num1, num2);
printf("덧셈 결괴 : %d\n", result);
return 0;
}
두 함수 모두, 맨 마지막에 return이 들어가 있다.
저 return의 기능을 알아야 다음 매개변수와 반환값 부분을 이해하는 데 더 쉬울 것이다.
return의 역할 또는 의미는 두 가지이다.
1. 값을 반환한다
2. 함수를 종료한다.
아까 전에 함수에서, x에 값에 따라서 y의 값이 하나로 정해진다 라고 말을 하지 않았는가? 함수를 통해서 y의 값이 나왔는데, 이 y의 값이 이거에요! 라고 알려주는 역할, 즉 반환한다 라고 한다. 결과값이 나왔는데, 이거에요! 이거 쓰세요!
그리고 함수를 종료한다는 말 그대로, return이 만나버리면 저 함수를 탈출하고 그 함수는 더 이상 실행되지 않는다.
int add(int a, int b){
int c = a+b;
return c;
}
이 함수를 보면, a+b를 더한 값인 c를 결과값으로 반환을 하고 이 함수는 끝났다 라는 것을 얘기한 것이다.
그리고 보통 함수가 끝난 경우에는 자신이 호출되었던 위치로 다시 되돌아가게 되는데, 그 역할도 return이 해준다. 그러니까 return을 만나면 본인 함수 끝내버리고, 본인 호출한 위치로 되돌아가게 한다! 이렇게 이해하면 될 것 같다.
매개변수와 반환값
다시 add 함수를 확인해보자.
int add(int a, int b){
int c = a+b;
return c;
}
return에 대해서는 위에서 알아보았고, 그래서 return c를 하면 c라는 변수, 저기 함수상에서는 a+b를 한 결과값을 return 을 해준다고 하는데 저 c가 바로 반환값이 된다.
그러면 a, b를 계산해야하는데, 저 값들이 매개변수이다.
좀 더 자세히 알아보자.
(1) 반환값
아까 return 뒤에 있는 변수 또는 값이 반환된다고 했는데, 그래서 반환값이다.
그럼 여기서 한 번 보자. return c; 에서 c의 자료형은 무엇인가? int c가 위에 있기에, c는 int 형이다.
그럼 add 앞에 있는 자료형은 뭐인가? int형이다.
그러니까 반환하는 c의 자료형과 함수 이름 앞에 있는 자료형같다는 거인가? 맞다. 반드시 그래야만 한다.
// 함수 정의
반환형 함수명(자료형 매개변수1, 자료형 매개변수2, ...) {
return 반환값;
}
반환형이라는 것은 결국엔 반환값의 자료형과 일치한다는 것.
반환값인 c를 만약에 char형으로 내버리고 싶다?
그러면
char add (int a, int b){
char c;
return c;
}
이렇게 해줘야한다.
근데 아무것도 반환 하고 싶지 않을 경우도 있다.
#include <stdio.h>
void printMessage() {
printf("Hello, World!\n");
}
int main() {
printMessage(); // 반환값이 필요 없는 함수 호출
return 0;
}
이럴 경우, 반환값이 없다라는 void 를 이용하면 된다.
단순히 출력을 위해 있는 것은 반환값이 필요없기 때문이다.
그래도 습관을 들이기 위해서
#include <stdio.h>
void printMessage() {
printf("Hello, World!\n");
return;
}
return 은 쓰는 것을 추천한다.
(2) 매개변수
매개변수는 함수가 호출될 때 외부에서 전달받는 입력값이다. 함수가 작업을 수행하기 위해 필요한 데이터를 전달받기 위해 사용된다.
int add(int a, int b){
int c = a+b;
return c;
}
여기서 정수형 a와, 정수형 b를 외부로 부터 입력받는 다 라는 것을 알려준다.
그리고 메인함수에서
int main(){
int num1, num2;
printf("a의 값을 입력하세요 : ");
scanf("%d", &num1);
printf("b의 값을 입력하세요 : ");
scanf("%d", &num2);
int result = add(num1, num2);
printf("덧셈 결괴 : %d\n", result);
return 0;
}
이렇게, int형 두 개를 넣어주는 것이다.
그래서
int c = add(3, 4)
이렇게 해주어도 상관없다. add에서 반환된 것은 c로 넘어가고, add로 값을 넘겨줄 떄는 정수형 3, 4 두개를 넘겨주었기 때문에. 이것을 매개변수라고 한다.
함수에서 선언한 매개변수 개수 만큼 똑같이 실행할 떄 개수만 맞춰서 넣어주면 되는 거다.
매개변수가 필요없는 함수면
#include <stdio.h>
void printMessage() {
printf("Hello, World!\n");
return;
}
이와 같이 아무것도 안 넣어주면 된다.
파이썬 같은 경우는 매개변수가 들어오지 않을 경우에는 그냥 디폴트값 때리는 경우가 있는데 C언어는 그런 거 없다. 뭐 오버로딩 같은 개념도 있는데 C는 그런 거 없으니까 안심해라! 너굴맨이 처리했다!
가변인자
위에서 매개변수에 대해서 알아보았는데, 만약에 매개 변수가 몇 개가 들어올지 모르는 상황이 발생할 수도 있다. 그럴 경우에 즉, 함수가 호출될 때 정해지지 않은 수의 인자를 받을 수 있게 해주는 기능이 필요한데, 이를 가변인자라고 한다.
C언어에는 이 가변인자를 위한 라이브러리가 이미 구현이 되어있다.
#include <stdarg.h>
이제 어떻게 쓰는 지 알아보자.
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...);
int main(){
int a = sum(5, 1, 2, 3, 4, 5);
int b = sum(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
int sum(int count, ...){
}
일단 이렇게 구조만 설명을 하자면
매개변수 부분에다가 매개변수로 총 몇 개 들어올 건지 알려주는 count, 그리고 뒤에 가변인자라는 것을 표기하기 위해 ...이 들어간다.
가변인자만 받기 위해 int sum(...) 이렇게 할 수는 없다. 반드시 기본 매개변수 1개는 기본적으로 있어야한다.
그래서 보통 가변인자의 개수를 맨 앞에다가 둔다. 근데 필요에따라 굳이 그렇게 가변인자의 개수를 적지 않아도 되긴 할 듯 하다.
가변인자를 사용하기 위해선 stdarg안에 있는 내용을 알아야하는데
- va_list:
- 가변 인자 목록을 선언하는 데 사용. 이 변수는 가변 인자를 관리하는 데 필요하다.
- va_start(va_list, last_fixed_arg):
- 가변 인자 목록을 초기화한다. last_fixed_arg는 가변 인자 앞에 오는 마지막 고정된 매개변수를 지정한다.
- va_arg(va_list, type):
- 가변 인자 목록에서 다음 인자를 가져온다. 가져올 인자의 자료형(type)을 지정해야 한다.
- va_end(va_list):
- 가변 인자 처리가 끝났음을 알리고, va_list를 정리한다..
그니까,
int sum(int count, ...) {
va_list args; // 가변 인자 목록 선언
va_end(args) //가변인자 종료
return total;
}
이렇게 가변인자 목록 선언, 종료가 있어야한다. 메모리 때문에.
이제 중간 부분 코드를 짜보면
int sum(int count, ...){
va_list args;
va_start(args, count);
int total = 0;
for (int i=0;i<count;i++){
total += va_arg(args, int);
}
va_end(args);
return total;
}
va_start 한 다음에 첫 번째 매개변수 값을 넘겨주는 건, 이 매개변수의 다음 값부터 처리하라는 이야기이다.
그러니까 첫 번째 값은 count로 받을 거고, 저 값을 va_start에다가 넘겨주면 바로 2번째 있는 거부터 가변인자라고 애가 인식을 하고 count(매개변수의 개수)만큼 돌면서 더해주는 방식이다.
그래서 count 개수 만큼 뒤에 있는 가변인자들을 돌면서 모두 더해주는 코드를 작성하였다.
va_arg (args, 자료형)을 통해서, 어떤 형식으로 받을 것인지 지정해주면 알아서 가변인자들을 차례대로 돌아준다.
그래서 전체 코드
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...);
int main(){
int a = sum(6, 1, 2, 3, 4, 5, 6);
int b = sum(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
int sum(int count,...){
va_list args;
int total = 0;
va_start(args, count);
for (int i=0;i<count;i++){
total += va_arg(args, int);
}
va_end(args);
return total;
}
실행을 시켜주면
이렇게 된다.
매개변수를 아무리 입력해도 3개만 처리하고 싶으면 count 값에다가 3만 입력하면 되겠죠?
도전
1. 빼기 구현해보기.
#include <stdio.h>
double sub(double a, double b);
int main(){
double num1, num2;
printf("첫 번째 값을 입력하세요 : ");
scanf("%lf",&num1);
printf("두 번째 값을 입력하세요 : ");
scanf("%lf", &num2);
double result = sub(num1, num2);
printf("결과 : %.2lf\n", result);
return 0;
}
double sub(double a, double b){
double c = a - b;
return c;
}
2. 가변인자를 이용하여 n개 정수의 합을 구하는 함수 만들기
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...);
int main(){
int count;
printf("몇 개의 수를 더할까요? : ");
scanf("%d", &count);
int result;
result = sum(count, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25);
printf("1부터 %d까지의 합은 %d입니다\n", count, result);
}
int sum(int count, ...){
va_list args;
va_start(args, count);
int total = 0;
for (int i=0;i<count;i++){
total +=va_arg(args, int);
}
va_end(args);
return total;
}
에필로그
어렵네...다음 글에서 이제 함수 2편 ㄱㄱ
'KnockOn' 카테고리의 다른 글
[KnockOn] Linux/Ubuntu C언어 포인터 (0) | 2024.11.21 |
---|---|
[KnockOn] Linux/Ubuntu C언어 함수 -2 (0) | 2024.11.20 |
[KnockOn] Linux/Ubuntu C언어 배열 (0) | 2024.11.17 |
[KnockOn] Linux/Ubuntu C언어 조건문, 반복문 (2) | 2024.11.15 |
[KnockOn] Linux/Ubuntu C언어 입출력함수 (1) | 2024.11.14 |