C 함수는 선언, 정의, 호출, 전달 방식이 같이 보여야 이해가 쉽습니다. 함수는 값을 계산하거나 동작을 수행하고, 기본 호출 모델은 값 복사입니다.
- 함수 원형이 왜 필요한지 다시 정리하고 싶다
- 값 전달과 포인터 전달이 어디서 갈리는지 보고 싶다
- 반환값 하나와 출력 매개변수 여러 개를 어떻게 나누는지 확인하고 싶다
숏컷 코드
기본 전달은 값 복사이고, 원본 수정이나 다중 결과가 필요하면 포인터를 넘깁니다.
int max_of(int a, int b);
int best = max_of(left, right);
void log_message(const char *msg);
log_message("done");
void divide(int a, int b, int *q, int *r);
divide(10, 3, &q, &r);문법
함수의 가장 기본 형태는 아래처럼 읽습니다.
return_type function_name(parameter_list) {
/* body */
}정의보다 먼저 호출하려면 원형 선언이 필요합니다.
int max_of(int a, int b);
void log_message(const char *msg);실제 호출은 아래처럼 이어집니다.
int best = max_of(left, right);
log_message("done");즉 함수 카드는 적어도 선언, 정의, 호출 세 층이 같이 보여야 합니다.
호출이 빠지면 함수 시그니처만 보고 끝나기 쉽습니다. C에서는 선언보다 "어떤 값이나 주소를 넘겨서 실제로 어떻게 부르는가"까지 같이 봐야 감각이 빨리 잡힙니다.
값 전달
int max_of(int a, int b) {
return (a > b) ? a : b;
}
int best = max_of(left, right);인자 a, b는 호출자 변수의 별명이 아니라 복사본입니다. 함수 안에서 값을 바꿔도 호출자 원본은 그대로입니다.
포인터 전달
void divide(int a, int b, int *q, int *r) {
*q = a / b;
*r = a % b;
}
int q = 0;
int r = 0;
divide(10, 3, &q, &r);원본 수정이나 다중 결과는 주소를 넘겨서 처리합니다.
int q = 0;
int r = 0;
divide(10, 3, &q, &r);
printf("%d %d\n", q, r);체크포인트
- 기본 전달은 값 복사입니다.
- 원본을 바꾸거나 여러 결과를 돌려주려면 포인터를 넘깁니다.
- 함수가 아래에서 정의되면 원형 선언이 필요합니다.
- 결과 하나면 반환값, 여러 결과면 출력 매개변수를 먼저 봅니다.
void함수는 값을 돌려주지 않고 동작 자체가 목적일 때 씁니다.
즉:
- 계산 결과 하나면 반환값
- 호출자 상태를 바꾸면 포인터 인자
- 둘 다 있으면 반환값과 출력 매개변수를 같이 설계
void log_message(const char *msg) {
puts(msg);
}주의할 점
원형 선언과 정의의 타입이 다르면 호출 규약이 어긋나서 위험한 동작으로 이어질 수 있습니다.
double compute(int x);
int compute(int x) {
return x * 2;
}- 큰 구조체를 값으로 넘기면 복사가 발생합니다.
- 읽기 전용인데 복사 비용이 부담되면
const struct T *전달도 같이 검토하는 편이 좋습니다. - 반환값이 있는 함수는 모든 경로에서 값을 돌려줘야 합니다.
참고 링크
1 sources