HOWTO · C

C의 MIN 및 MAX 함수

매크로나 루프를 사용하여 C에서 MIN 및 MAX 기능을 구현할 수 있습니다.

이 페이지의 내용

C에서 두 값 중 더 작거나 큰 값을 찾으려면 MIN 또는 MAX 매크로를 정의하거나 함수를 작성할 수 있습니다. 표준 C에는 범용 내장 MIN 및 MAX 함수가 없습니다. 배열에서는 첫 번째 요소로 결과를 초기화한 뒤 나머지 요소를 순회하면 됩니다.

C에는 MIN과 MAX가 내장되어 있을까요?

C 표준 라이브러리는 범용 MIN 및 MAX 함수를 정의하지 않습니다. 이러한 이름은 보통 프로젝트에서 매크로나 일반 함수로 제공합니다. 인수에 부작용이 있다면 함수 쪽이 동작을 예측하기 쉽고, 안전하게 평가되는 값에는 간단한 매크로를 쓰면 코드가 간결해질 수 있습니다.

#define 지시문은 전처리기 매크로를 만듭니다. 전처리기가 매크로 호출을 만나면 컴파일러가 프로그램을 처리하기 전에 대체 텍스트로 바꿉니다. 반면 함수 호출은 컴파일된 코드를 실행하며, 인수는 C의 함수 호출 규칙에 따라 평가됩니다.

두 값에 사용할 MIN 및 MAX 매크로 정의

부작용이 없는 두 정수 식을 다룰 때는 각 매개변수와 대체 식 전체를 괄호로 묶습니다. 이렇게 하면 호출자가 a + b 같은 식을 전달해도 의도한 연산자 묶음이 유지됩니다.

#include <stdio.h>

#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))

int main(void) {
  printf("Min = %d\n", MIN(0, 10));
  printf("Max = %d\n", MAX(0, 10));
  return 0;
}
Min = 0
Max = 10

조건 연산자 ?:는 비교 결과가 참이면 ? 뒤의 식을 선택하고, 거짓이면 : 뒤의 식을 선택합니다. 괄호는 연산자 우선순위를 처리하지만 반복 평가를 막지는 않습니다. MIN(a, b)에서는 선택된 인수가 대체 텍스트에서 다시 나타납니다. 따라서 i++나 상태를 바꾸는 함수 호출 같은 인수는 피하세요. 이 매크로는 단순한 값에 사용하는 것이 적합하며, 인수에 부작용이 있을 수 있다면 함수를 사용하세요.

인수를 한 번만 평가하는 GNU C 매크로 사용

GNU C는 문 식(statement expression)과 typeof를 지원합니다. 이 예제에서는 __typeof__라고 표기합니다. 아래 매크로는 각 인수를 임시 변수에 저장한 다음 그 값을 비교합니다. 각 인수 식은 초기화 과정에서 한 번씩 평가됩니다. 이는 GNU C 확장이므로 ISO C에서는 이식할 수 없으며, GNU 확장을 활성화하는 컴파일러 옵션이 필요할 수도 있습니다.

#include <stdio.h>

#define MAX(x, y)                    \
  ({                                 \
    __typeof__(x) _max_x = (x);      \
    __typeof__(y) _max_y = (y);      \
    _max_x > _max_y ? _max_x : _max_y; \
  })

#define MIN(x, y)                    \
  ({                                 \
    __typeof__(x) _min_x = (x);      \
    __typeof__(y) _min_y = (y);      \
    _min_x < _min_y ? _min_x : _min_y; \
  })

int main(void) {
  int a = 4;
  int b = 9;
  printf("Min = %d\n", MIN(a++, b));
  printf("Max = %d\n", MAX(a, b));
  return 0;
}
Min = 4
Max = 9

이 패턴은 < 또는 >로 비교할 수 있는 일반적인 스칼라 식에 유용합니다. 하지만 모든 타입을 처리하는 범용 함수는 아닙니다. 서로 호환되지 않는 타입, 의미 있는 순서가 없는 포인터, 해당 연산자를 지원하지 않는 타입에는 다른 설계가 필요합니다. 조건식에도 C의 일반적인 타입 변환 규칙이 적용됩니다. 이식 가능한 코드를 원한다면 타입이 지정된 함수를 사용하거나 지원할 타입별로 함수를 작성하세요.

배열에서 최솟값과 최댓값 찾기

배열을 순회하면서 현재 최솟값과 최댓값을 유지하고 필요할 때 갱신합니다. 실제 배열 요소를 기준으로 결과를 구하도록 두 값을 첫 번째 요소로 초기화합니다. 아래 예제는 기존 배열 예제와 같은 값을 사용하며 배열을 한 번 순회합니다.

#include <stdio.h>
#include <stddef.h>

void find_min_max(const int values[], size_t length, int *minimum, int *maximum) {
  *minimum = values[0];
  *maximum = values[0];

  for (size_t i = 1; i < length; ++i) {
    if (values[i] < *minimum) {
      *minimum = values[i];
    }
    if (values[i] > *maximum) {
      *maximum = values[i];
    }
  }
}

int main(void) {
  int values[] = {6, 3, 15, 9, 10, 50, 99, 11};
  size_t length = sizeof(values) / sizeof(values[0]);
  int minimum;
  int maximum;

  find_min_max(values, length, &minimum, &maximum);
  printf("Max number = %d\n", maximum);
  printf("Min number = %d\n", minimum);
  return 0;
}
Max number = 99
Min number = 3

이 함수는 비어 있지 않은 배열과 유효한 결과 포인터를 전제로 합니다. 배열 매개변수는 포인터로 조정되므로 호출자가 길이를 전달해야 합니다. find_min_max 안에서 sizeof(values)를 사용하면 원래 배열이 아니라 포인터의 크기를 구합니다. 여기서는 values가 여전히 배열인 main에서 sizeof로 요소 개수를 계산합니다. 배열의 전체 크기를 요소 하나의 크기로 나누면 됩니다.

부동소수점 데이터를 사용할 때는 배열, 최솟값과 최댓값 변수, 함수 매개변수의 타입을 일관되게 맞추세요. printf의 변환 지정자는 승격된 인수 타입과 일치해야 합니다. double에는 %f를 사용합니다. 여러 숫자 타입을 처리하는 함수가 필요하다면 int만 float으로 바꾸기보다 타입에 맞는 함수를 작성하거나 신중하게 설계한 추상화를 사용하세요.