Forensic/혼공C 챌린지

4주차 과제

Ron Weasley 2022. 8. 4. 13:57

표준 라이브러리 함수

함수는 기능을 수행하는 코드 단위를 말합니다. 지금까지 사용해왔던 main 함수가 대표적인 함수입니다.

그리고 입.출력을 할 때 사용하던 printf()와 scanf()도 함수이고 특정 기능을 미리 약속하고 프로그램에서 바로 사용할 수 있게 구현되어 있는 함수입니다. 이런 함수처럼 프로그램에서 바로 사용할 수 있게 구현되어 있는 함수를 표준 라이브러리 함수라고 합니다. 그리고 이런 함수는 stdio.h 헤더 파일에 포함되어 있습니다.

 

함수를 정의할 때 필요한 3가지가 있습니다. (꼭 기억하셔야 합니다)

함수 정의 : 함수를 실제 코드로 만드는 것이며 기능을 구현합니다.

함수 호출 : 함수 호출을 해야지 함수를 사용할 수 있습니다.

함수 선언 : 프로그램의 상단에서 어떤 함수를 사용할 것이라고 컴파일러에 정보를 주는 역할을 합니다.

 

함수 정의

지금까지는 main 함수 하나로 프로그램을 작성했습니다. 이번 시간부터는 main 함수와 또 다른 함수가 프로그램의 전체 기능을 나누어 수행하도록 프로그램을 만들겠습니다. 새로운 함수를 만든느 방법도 main 함수를 만드는 방법과 크게 다르지 않습니다. 다만 함수에 기능에 맞춰서 이름을 선정해주는 것이 중요합니다.

1. 함수의 기능에 맞는 이름은 무엇인가? ex) sum

2. 함수가 기능을 수행하는데 필요한 데이터는 무엇인가? ex) 매개변수

3. 함수가 수행돤 후의 결과는 무엇인가? ex) 반환형

 

함수 원형

함수 원형은 함수 정의를 말합니다. 예제 코드를 보겠습니다.

함수명은 기능을 충분히 예상할 수 있도록 적절한 이름을 선택하며, 변수 선언 규칙에 따라 만듭니다. 두 정루르 더하는 함수이니 add 또는 sum으로 함수명을 만들면 됩니다.

 

매개변수는 함수가 처리할 데이터를 저장하는 변수로 함수명 옆의 괄호 안에 선언합니다. sum 함수는 2개의 정수를 더하는 함수이므로 int 형 변수를 2개 선언합니다. 이때 매개변수의 자료형이 같아도 콤마로 구분해서 따로 선언합니다.

 

반환형은 함수가 기능을 수행한 후에 호출한 곳으로 돌려줄 값의 자료형을 적습니다. 즉, sum 함수는 두 정수의 합을 반환해야 하므로 int를 사용합니다.

 

함수 호출

지금까지 함수를 만들어봤는데, 함수는 만드는건 얼마든지 만들 수 있지만 만든 함수가 자동으로 실행되는 것은 아니기 때문에 함수를 사용하려면 함수 호출이 필요합니다.

 

[위 직접해보는 코딩] 에서 result = sum(a, b); 이 부분이 main 함수 안에서 sum 함수를 호출하는 부분이다.

 

함수를 호출할 때에는 이름을 사용하며 함수에 필요한 데이터를 괄호 안에 넣어주는데, 이를 인수(argument)라고 합니다. sum 함수를 호출해서 a와 b를 더하여면 함수명과 함께 괄호 안에 a, b를 넣고 호출합니다. 인수는 상수나 변수를 쓸 수 있고 수식을 사용하면 수식의 결괏값이 인수로 쓰입니다.

 

함수를 호출할 때 이렇게 입력된 인수 a, b의 값은 호출된 함수의 매개변수에 복사되어 사용합니다.

sum(a, b)가 호출될 때 a와 b에 들어가는 값이 인수 입니다.

그리고 int sum(int x, int y)에서 a의 값이 x에 들어가고 b의 값이 y에 들어가는데, 호출된 함수 x와 y에서는 받은 인수를 매개변수라고 합니다. 결국에 sum 함수가 호출되면 main 함수의 실행은 잠시 멈추고 sum 함수 정의 부분에 있는 코드가 실행이 되며, 이때 인수는 함수의 매개변수에 순서대로 복사되어 sum 함수 안에서 사용됩니다.

 

함수 반환

호출된 함수가 실행을 끝내고 값을 반환할 때 return 문을 사용합니다. 위 코드를 보면 sum 함수는 x와 y를 더하여 temp에 저장한 뒤 함수의 실행을 끝내고 호출한 곳으로 돌아가기 위해 return문을 사용합니다. 이때 temp의 값을 호출한 곳으로 반환합니다. 이후 main 함수는 반환된 값을 대입 연산으로 result에 저장하고 잠시 중단했던 함수의 나머지 부분을 실행합니다.

 

컴파일러는 함수를 호출할 때 반환값을 저장할 공간을 미리 준비해두고, 이 공간은 컴파일러가 별도로 확보하는 공간이며 식별할 수 있는 이름이 없으므로 계속 사용할 수 없습니다. 대신 저장된 반환값을 대입 연산으로 다른 변수에 복사하거나 수식에 바로 사용하는 것은 가능합니다. 쉽게 말하면, 위 코드에서 30이라는 값이 반환이 되었는데, 이 30이라는 반환값을 계속 사용하지는 못하지만 sum이라는 함수 호출을 하면서 sum(a, b) * 2 라는 대입 연산을 입력을 했다면 반환값이 30이 아니라 60이 되어서 나오겠지요?? 그런 뜻입니다.

 

함수 선언

함수 선언은 컴파일러가 새로 만든 함수를 인식할 수 있도록 알리는 역할을 합니다.

int sum(int x, int y);
반환형 : int
함수명 : sum
매개변수 : int x, int y

위 인용된 부분은 아까 위에서 말로써 설명을 했습니다. 하지만 차이점이 있다면 함수 원형과는 다르게 세미콜론이 있습니다.

 

단, 함수 선언이 없으면 함수 정의는 항상 함수 호출 이전에 있어야 합니다. 왜냐하면 함수를 미리 선언하지 않으려면 항상 호출하는 함수 앞에 정의를 해야 합니다. 이때 여러 함수 간에 호출 관계가 엉켜 있으면 순서에 맞게 정의하는 일이 쉽지 않습니다. 따라서 필요한 함수를 main 함수 밑에 차례로 만들고 main 함수 앞에는 모든 함수를 선언하여 함수의 종류와 원형을 한눈에 파악하고 자유롭게 호출할 수 있도록 작성하는 것이 좋습니다. 마지막으로 int형을 반환형으로 넣어줬는데 실수가 반환되면 에러가 납니다.

<확인 문제>

1. 다음은 두 실수의 평균을 구하는 함수를 호출하는 코드입니다. 함수 정의에서 빈칸에 적당한 내용을 채우세요.

2. 187cm를 미터 단위로 혼산하여 출력하는 프로그램을 작성하세요. 단, cm를 m로 환산하여 반환하는 함수를 만들고 함수 호출을 통해 구현합니다.

 

여기까지가 이제 함수의 기본 원리?? 라면 이제는 여러가지 함수 유형을 살펴보겠습니다.

 

함수 원형은 "반환형 함수명(매개변수)" 라고 설명을 드렸었습니다. 하지만 언제나 함수에는 반환형과 매개변수가 존재해야 하는 것은 아닙니다. 앞에서 설명을 할 때 사용했던 sum 함수는 두 수를 더하는 함수였기에 매개변수가 필요했습니다. 그런데 단순히 키보드 입력 값을 확인하고 양수를 반환하는 함수라면 매개변수가 있을 필요가 없습니다. scanf 함수로 입력을 받으면 되니까요!! 이처럼 여러가지 유형의 함수가 있다 라고만 생각하시고 바로 넘어가보겠습니다.

 

매개변수가 없는 함수

매개변수가 없는 함수는 키보드로 수를 입력하면 양수를 반환하는 함수를 만든다면 호출한 함수로부터 값을 받을 필요가 없으므로 매개변수도 필요 없습니다. 함수의 매개변수 자리에는 void를 사용합니다.

13행의 함수 원형을 살펴보시면 괄호 안에 void를 넣어 매개변수가 없음을 표시했습니다. 괄호만 사용하는 것도 가능은 하지만 void를 넣어주어 매개변수가 없음을 명시적으로 표현하는 것이 좋다고 생각하기 때문에 넣어줬습니다. get_num 함수는 매개변수가 필요 없지만 키보드로 입력받은 값을 호출한 함수로 반환을 하기 때문에 반환형(int)는 있어야 합니다. 반환형이 있다면 반환값도 있어야겠죠?? 

 

하지만 사람들이 장난꾸러기라면 음수를 넣을수도 있습니다. 그래서 음수를 대비해서 예외처리에 대해서 살짝 배우겠습니다.

입력한 값이 음수일 경우 무한 루프를 돌게끔 만들어 줬습니다.

프로그램 개발자는 프로그램 사용자가 종종 프로그램을 만든 의도와 다른 사용법으로 프로그램을 비정상 종료를 시키는데, 이처럼 프로그램을 실행하다가 발생할 수 있는 예외 상황에 대비하여 코드를 추가하는 것이 "예외 처리"입니다.

 

반환값이 없는 함수

함수는 기능에 따라 형태가 결정됩니다. 데이터를 받아서 단지 화면에 출력하는 함수라면 특별히 반환값이 필요 없습니다.

예를 들어 문자와 숫자를 인수로 받으면 문자를 숫자만큼 화면에 출력하는 함수를 생각해보겠습니다. 화면에 출력한 내용이 함수가 수행한 결과이므로 호출한 곳으로 특별히 값을 반환할 필요가 없습니다. 매개변수의 빈자리에 void를 사용했던 것처럼 이때는 반환형의 자리에 void를 사용합니다.

12행이 print_char의 함수 원형입니다. 문자와 숫자를 받아야 하므로 매개변수는 있으나 반환형은 void를 사용합니다.

또한 반환값이 없으므로 21행의 return문은 값 없이 단독으로 사용합니다.

 

return의 역할이 값을 돌려주는 일인데, 돌려줄 값이 없으므로 자연스럽게 혼자 쓰입니다. 심지어 return문 자체를 생략하는 것도 가능합니다. return문 없이도 함수의 코드를 모두 수행하면 호출한 곳으로 자동으로 돌아갑니다.

"반환값이 없는 함수는 return문을 생략할 수 있다" << 요 키워드만 생각을 해주시면 될 것 같습니다.

 

또 프로그램 사용자는 장난꾸러기라 이상한 값을 넣어보잖아요! 아까 sum에서는 수식이 가능했지만 반환값이 없는 함수는 호출문장을 수식으로 사용할 수 없습니다. print_char('!', 5) + 10 같이 수식으로 만들면 에러가 발생합니다.

매개변수와 반환값이 모두 없는 함수

매개변수와 반환값이 모두 없는 함수도 만들 수 있습니다. 일정한 문자열을 여러 번 출력하는 함수라면 매개변수와 반환값이 모두 필요없습니다. 이때 함수의 매개변수와 반환형에 모두 void를 선언해줍니다.

성공적으로 잘 출력이 되네요 ㅎㅎ

 

재귀함수 호출

아마...이때까지 배운 것들과는 차원이 다를 정도로 이해가 어려웠습니다.

간단하게 정의를 하면 자기 자신을 호출하는 겁니다. 예제로 바로 보겠습니다.

이렇게 자기 자신을 호출하는데, 인수가 없으면 무한 루프를 돌다가 프로그램을 자동적으로 꺼버립니다. 그래서 인수를 가장 먼저 출력이 되는 main함수 내부에 fruit()에 인수를 줘야합니다. 결국엔 자신의 함수를 몇번 호출할 건지 횟수를 조건으로 적어주면서 호출을 해야 합니다. 3번만 호출하는 코드를 보겠습니다.

count가 3이되면 반환을 한다는 것을 알 수 있습니다.

여기서 드는 의문점은 반복문을 사용해도 되지 않나요? 라고 할 수 있는데 재귀함수와 반복문의 차이점은 다음과 같습니다.

재귀호출 함수는 최초 호출한 곳으로 돌아가는 것이 아니라 이전에 호출했던 곳으로 돌아가기 때문입니다.

결국엔 재귀 호출은 하나의 함수에서 코드를 반복 실행하는 듯하지만 실제로는 새로운 함수를 실행하는 것과 동일합니다. 재귀호출 함수는 경우에 따라 복잡한 반복문을 간단히 표현할 수 있으나 코드 읽기가 쉽지 않고 반복 호출되면서 메모리를 많이 사용하므로 제한을 두고 쓰는 것이 적합합니다. ( 무슨 말인지..??)

 

<확인 문제>

1. main 함수와 실행결과를 참고하여 1부터 일정 수까지의 합을 구하는 sum 함수를 만들고 프로그램을 완성하세요.

2. 다음 프로그램의 실행결과를 예상해보세요.

위 코드의 실행결과는 9입니다.

이유에 대해서 설명을 좀 드리겠습니다. 일단 1, 2행에 func함수와 poly함수를 선언한 것을 볼 수 있습니다.

그리고 main에서 func함수에 -3의 인수를 넣어줬습니다. 그럼 func 함수에 -3을 인자값으로 받았고 poly 함수를 호출할 때 -3이라는 인자값으로 전달을 또 했습니다. poly에서 계산식인 (2 * (-3) * (-3)) + (3 * -3)을 해보면 (-6) * (-3) = 18 + 3 * (-3) = 18 + -9가 되므로 9가 반환되어 호출했던 함수인 func 함수로 돌아와서 if로 조건을 확인합니다. 반환된 값이 0보다 크거나 같다면 바로 반환을 하여 func 함수가 호출된 곳으로 돌아갑니다. 그래서 최종적으로 반환된 값인 9가 출력이 되는 것입니다.

 

 

'Forensic > 혼공C 챌린지' 카테고리의 다른 글

6주차 과제  (0) 2022.08.16
5주차 과제  (0) 2022.08.12
3주차 과제  (0) 2022.07.24
2주차 과제  (0) 2022.07.18
1주차 과제  (0) 2022.07.06