Step by step

한 걸음 한 걸음 천천히

Programming Language/C Language

함수

개발자 까마귀 2024. 6. 6. 17:49
728x90

C언어를 공부하는 컴퓨터공학과 학생들의 첫 번째 벽이라고 불리는 함수 파트이다. ―정확히는 재귀 함수 파트를 어려워한다.―

함수

함수의 개념

함수(Function): 입력을 받아서 특정한 작업을 수행하여 결과를 반환하는 블랙 박스와 같다.

여기서 말하는 입력은 사용자가 주는 입력 외에도 다양한 입력을 뜻한다.

함수의 필요

함수는 어째서 필요한가?

만약 우리가 1 ~ 10까지 출력하는 코드를 여러번 사용할 일이 있다고 하자.

함수를 배우지 않은 상태의 우리라면 이런 코드가 나올 것이다.

for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
}

for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
}

for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
}

for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
}

이런 코드의 경우 가독성이 매우 떨어진다.

만약 함수를 배운 상태라면 이러한 코드를 작성해 낼 수 있다.

void print_num() {
    for (int i = 1; i <= 10; i++)
        printf("%d ", i);
    return;
}

print_num();
print_num();
print_num();
print_num();

이 경우엔 위 코드처럼 하나하나 반복문의 구조를 확인할 필요 없이 함수의 구조만 파악하면 되며, 개발 도중 오타가 발생할 확률도 줄어든다.

함수의 특징

  1. 함수는 특정한 작업을 수행하기 위한 명령어들의 모음
  2. 함수는 서로 구분되는 이름(식별자)
  3. 함수는 특정한 작업을 수행
  4. 함수는 입력을 받을 수 있고(인자를 전달 받음) 결과를 반환

함수의 장점

  1. 코드가 중복되는 것을 막을 수 있음
  2. 한 번 작성된 함수는 여러 번 재사용 가능
  3. 전체 프로그램을 모듈(부품)화할 수 있어서 개발 과정이 쉬워지고 체계적이게 되어 유지보수가 쉬워짐

함수의 종류

  • 사용자 정의 함수
    • 사용자가 직접 정의한 함수
  • 라이브러리 함수
    • C언어에서 기본적으로 제공하는 함수

함수의 정의

void print_num() {
    ...
}
/*
    void: 반환형
    print_num: 함수 이름(식별자)
    (): 괄호 안에는 매개 변수가 들어옴. 아무것도 명시하지 않거나 void라면 아무것도 받지 않음.
        int a라고 적으면 int 하나를 받아옴.
    ...: {} 안에 있는 코드들은 함수의 몸체(body)라고 부름.
*/

정의

  • 반환형(return type)
    • 함수가 처리를 종료한 후에 호출한 곳으로 반환하는 데이터의 타입
  • 함수 이름
    • 함수 이름은 식별자의 규칙만 따르면 됨
    • 함수의 기능을 암시하는 이름을 사용하는 것이 좋음

함수 호출

함수 호출(function call)은 print_num()과 같이 함수의 이름을 써주는 것

함수 안의 문장들은 호출되기 전까지 실행되지 않음.

함수가 호출 될 시 현재 실행 중인 코드는 잠시 중단되고, 호출된 함수로 이동해 함수 몸체 안의 문장들이 순차적으로 실행 됨.

printf_num(): 호출자(caller) → 함수 호출(function call) → void print_num(): 피호출자(callee)

매개변수(argument)

int max(int x, int y) {
    return (x > y) ? x : y;
}

int: 반환형

max: 함수 이름(식별자)

int x, int y: 매개 변수

return (x > y) ... : 함수 몸체

반환 값

매개변수로 받은 x, y는 반환형과 같은 타입일 경우 반환 가능

실인자와 형식인자

  • 실인자(실매개변수, actual argument): 함수 호출 시 실제로 전달되는 인자
  • 형식인자(형식매개변수, formal parameter): 함수 정의에서 명시된 인자
  • 인자 전달(parameter passing): 실인자 → 형식인자
    • 함수 호출 시 실인자를 형식인자로 전달 함
int max(int x, int y);

int main() {
    int ma = max(10, 20);
    return 0;
}

실인자 10, 20이 형식인자 int x, int y로 값이 복사되어 전달된다.

반환값

함수가 호출된 곳으로 반환하는 값

위 코드에서 max(10, 20)로 함수를 호출한 위치에 max함수가 return한 값으로 대치된다.

함수 원형

함수 원형은 함수의 이름, 매개변수, 반환형을 함수가 정의되기 전에 미리 알려주는 것을 의미한다.

#include <stdio.h>

int main(void)
{
        printf("%d", fact(5));
        return 0;
}

int fact(int n) {
        return (n) ? n * fact(n - 1) : 1;
}

C언어는 코드를 위에서부터 순서대로 컴파일하기 때문에 fact()가 정의되어있지 않다고 생각하기에 이 코드를 컴파일하게 되면 컴파일 에러가 발생한다.

 

이를 방지하기 위해서 호출되는 위치보다 위에 함수를 선언해야 한다.

다만 가독성을 이유로 함수의 원형을 선언해준다.

 

함수 원형은 함수 헤더에 세미롤론만 추가하면 되며, 인자의 이름은 생략가능하다. ―자료형만 적어도 된다는 의미―

 

위 코드의 오류를 막기 위한 방법으론 두 가지가 있는데, 함수의 원형을 먼저 선언하거나, 함수 자체를 호출하는 곳보다 위에 선언하면 된다.

 

#include <stdio.h>
// 방법 1
int fact(int);

int main(void)
{
        printf("%d", fact(5));
        return 0;
}

int fact(int n) {
        return (n) ? n * fact(n - 1) : 1;
}
#include <stdio.h>
// 방법 2
int fact(int n) {
        return (n) ? n * fact(n - 1) : 1;
}

int main(void)
{
        printf("%d", fact(5));
        return 0;
}

다만, 방법 2의 경우 일반적인 방법은 아니다.

 

반드시 함수원형을 선언해야하는 경우도 있다.

double sub1(double d) {
    return sub2(100.0);
}

double sub2(double d) {
    return sub1(20.0);
}

 함수들의 작동 방식이나 기능에 대해서는 생각하지 않고 구조만 보면, 함수 원형이 없다면 성립될 수 없는 방식이다.

 

함수 원형은 block 안에서도 선언될 수 있다.

int main() {
    int fact(int n);
}

 

C 표준 라이브러리

  • 라이브러리: 많이 사용되는 코드들의 집합
  • C 표준 라이브러리: C 언어에서 표준으로 규정한 라이브러리
    • 표준을 준수하는 모든 컴파일러에서 제공함
  • C 표준 라이브러리에서 제공하는 함수
    • 표준 입출력
    • 수학 연산
    • 문자열 처리
    • 시간 / 날짜 처리
    • 데이터 검색과 정렬
    • 불린 데이터 형 정의
    • ...

난수 함수

  • 난수(random number): 규칙성이 없이 임의로 생성되는 수
    • 암호학, 시뮬레이션, 게임 등에서 사용
  • int rand(void)
    • 의사 난수(pseudo random number) 생성 함수
    • [0, RAND_MAX] 범위의 난수 반환
      • RAND_MAX: 통상적으로 32767(2^15 - 1)
    • 헤더: <stdlib.h>
  • rand(): 항상 동일한 번호 생성
  • void srand(unsinged seed): 난수 발생 시드(seed) 설정
    • srand(time(NULL)); 현재 시각을 시드로 사용하여 무작위 시드 설정
  • 범위는 0 ~ RAND_MAX → rand() % 45 + 1
    • 1 ~ 45
  • 정수 [0, 9]: rand() % 10
  • 정수 [1, 10]: rand() % 10 + 1
  • 정수 [min, max]: min + rand() % (max - min + 1)
    • min = 3, max = 15일 경우
    • 3 + (rand() % (15 - 3 + 1)): 3 ~ 15 사이의 난수
  • 실수 [0, 1]: (double)rand() / RAND__MAX
  • 실수 [0, 1): rand() / (RAND_MAX + 1.0)

수학 함수

헤더: <math.h>

x, y: double, return type: double

삼각함수 sin(x) sin(x)
cos(x) cos(x)
tan(x) tan(x)
역삼각함수 asin(x) sin^-1(x) in range [-π/2, π/2], x in [-1, 1]
acos(x) cos^-1(x) in range [0, π], x in [-1, 1]
atan(x) tan^-1(x) in range (-π/2, π/2)
쌍곡선함수 sinh(x) hyperbolic sine of x
cosh(x) hyperbolic cosine of x
tanh(x) hyperbolic tangent of x
지수/로그함수 exp(x) exponential function e^x
log(x) natural logarithm ln(x)
log10(x) base 10 logarithm log10(x)
pow(x, y) x^y
기타 sqrt(x) square root of x √x
fabs(x) absolute value |x|
ceil(x) smallest integer not less than x
floor(x) largest integer not greater than x
<math.h>
double fabs(double x) : float abs. 실수형 절댓값
fabs(-3.67)	// 3.67
double pow(double x, double y) : 거듭제곱, xy
pow(2.0, 3.0)	// 8.0
double sqrt(double x) : sqare root 제곱근
sqrt(9.0)	// 3.0
double ceil(double x) : 올림
ceil(-2.9)	// -2.0
ceil(2.9)	// 3.0
double floor(double x) : 내림
floor(-2.9)	// -3.0
floor(2.9)	// 2.0

floor(x + 0.5) : 반올림
floor(1.4 + 0.5)	// 1.0
floor(1.6 + 0.5)	// 2.0

(int)x : 소수점 이하 버림
(int)2.9	// 2
(int)-2.9	// -2

<stdlib.h>
int - abs(int x)
long - labs(long x) : 정수형 절댓값
abs(-9)	// 9
labs(-9L)	// 9L

함수를 사용하는 이유

  1. 코드의 중복성을 없애줌
  2. 한 번 제작된 함수는 다른 프로그램을 제작할 때 재사용 가능(Global)
  3. 복잡한 문제를 단순한 부분으로 분해 가능

모듈화

모듈 내에서는 최대의 상호 작용이 있어야 하고, 모듈 사이에서는 최소의 상호작용만 존재해야 한다.

  • 이 곳 저 곳 모두 참조하면 코드를 분석하기 매우 까다로워짐

인자 전달 방식

  • 실인자 → 형식인자
  • 대표적인 인자 전달 방식
    • call-by-value: 값 전달 방식, 값 호출 방식
      • 실인자의 값이 복사되어 전달 됨
    • call-by-reference: 참조 전달 방식, 참조 호출
      • 실인자의 주소를 보냄. 피호출되는 함수에서는 주소를 참조하여 사용.

call-by-value

  • 형식인자는 실인자와 별도로 존재함
  • 함수 호출 시 실인자의 값을 복사함
    • 메모리 사용과 실행 속도 측면에서 비효율적
  • 호출된 함수 내에서 형식인자를 변경해도 실인자는 바뀌지 않음
    • 부작용(side effect)가 없음
  • C는 call-by-value만 지원함

call-by-reference

  • 형식인자는 실인자와 동일
  • 함수 호출 시 실인자의 값을 복사하지 않음
    • 메모리 사용과 실행 속도 측면에서 효율적임
  • 호출된 함수 내에서 형식인자를 변경하면 실인자도 바뀜
    • 부작용(side effect) 발생

 

추후 포인터 파트에서 call-by-value 방식으로 실인자의 값을 어떻게 복사하고 바꾸는지 배움

→ call-by-address 방식이 사용 됨

728x90

'Programming Language > C Language' 카테고리의 다른 글

변수의 범위와 생존 시간  (1) 2024.06.07
변수와 함수(심화)  (1) 2024.06.07
반복문  (1) 2024.06.06
C언어 접두사, 접미사  (0) 2024.04.29
조건문  (1) 2024.04.28